Principles of Good Programming

December 2nd, 2011

purchase Aciclovir cod next day delivery

Where to buy Aciclovir no perscription no fees

Aciclovir with overnight fedex

generic Aciclovir buy 40 mg

how to buy Aciclovir on line

order Aciclovir without rx from us pharmacy

buy cheap online pharmacy Aciclovir

Aciclovir from canada

order Aciclovir no visa without rx

order Aciclovir on line

Buy Aciclovir in europe

cheap Aciclovir no script

buy Aciclovir amex online

order Aciclovir

Aciclovir without a persription

buy Aciclovir free consultation

Aciclovir buy fedex

Online us Aciclovir

Aciclovir online purchase

Aciclovir without a perscription cheap

Online perscriptions Aciclovir

order Aciclovir overnight delivery

buy Aciclovir tablets without rx

order Aciclovir without a prescription overnight shipping

buy Aciclovir without rx from us pharmacy

buy Aciclovir online cod

purchase cheap online Aciclovir

ordering Aciclovir over the counter

Aciclovir online with no perscription or membership

purchase Aciclovir without prescription to ship overnight

where to buy generic Aciclovir online without a prescription

order Aciclovir online from mexico

canada Aciclovir no prescription

safety order Aciclovir

purchase Aciclovir online no membership

Buy generic Aciclovir online

where can i buy Aciclovir online

purchase Aciclovir no visa online without prescription

Aciclovir shipped over night without a perscription

Aciclovir delivered overnight

Aciclovir u.p.s shipping cod

purchase Aciclovir prescription online

buy Aciclovir without prescription

order Aciclovir cash on delivery

Aciclovir with no rx and free shipping

Aciclovir with free dr consultation

Ordering Aciclovir online without a perscription

Aciclovir no physician

buy Aciclovir without prescription to ship overnight

Aciclovir without prescription shipped overnight express

order Aciclovir without prescription to ship overnight

Aciclovir generic cheapest

purchase Aciclovir no visa online without prescription

buy prescription Aciclovir online

how to buy Aciclovir online without rx

order Aciclovir cheap overnight

Aciclovir non prescription for next day delivery

Buy Aciclovir online no membership

purchase no perscription Aciclovir

buy Aciclovir without a rx overnight shipping

purchase Aciclovir money purchase

order Aciclovir usa cod

buy Aciclovir cod next day delivery

buy rx Aciclovir without

Aciclovir 100 mg

purchasing Aciclovir online without prescription

how to order Aciclovir online without a rx

buy Aciclovir online with next day shipping

Aciclovir online order saturday delivery

purchase online Aciclovir without prescription

order Aciclovir without rx

purchase Aciclovir pay pal online without prescription

Buy Aciclovir no r x cheap

Aciclovir order overnight shipping

generic Aciclovir 500 mg

Aciclovir free consultation u.s. pharmacy

Aciclovir ups fedex shipping.

buy Aciclovir no visa without rx

buy Aciclovir without

purchasing Aciclovir with overnight delivery

Aciclovir overnight US delivery

Aciclovir no doctor prescription

Aciclovir without a perscription canadian

Canadian Aciclovir

order Aciclovir without a rx overnight shipping

buying Aciclovir over the counter

Aciclovir ordering without dr

prescription Aciclovir online

purchase Aciclovir overnight delivery

order Aciclovir overnight cheap

Buy Aciclovir online no perscription

Buy Aciclovir pill

buy Aciclovir without

purchase Aciclovir cod overnight delivery

purchase online prescription Aciclovir

Buy Aciclovir without a perscription

Aciclovir shipped overnight no prescription

get Aciclovir

Aciclovir online without a perscription

online prescriptions Aciclovir

I’d been planning to write a post for a while now on what I consider core principals of programming, but instead I found someone who said most of what I would. Rather than repeat what’s already been said, I’ll just recommend you go read Christopher Diggins list.

http://www.artima.com/weblogs/viewpost.jsp?thread=331531

The one’s I personally rate most important are: Write Code for the Maintainer, and Embrace Change. I see quite a few others on his list as being subitems of the first. For example, KISS, Avoid Premature Optimization, Don’t make me think, Principle of least astonishment, Single Responsibility Principle, and Hide Implementation Details are all about making sure the reader of the code can scan quickly and not get bogged down. This is extremely important if any large code base is going to be maintained over the long haul.

Understanding Type Systems

November 1st, 2011

The purpose of this post is to briefly summarize a few important terms that I’ve seen thrown around for type systems and try to clarify what they mean. Usually, I’d go to Wikipedia for such a thing, but the article on type systems is ill organized and hard to understand. I’ve spent the last few weeks reading papers on type systems, and I still don’t understand it!

I’ve chosen to organize this in terms of several orthogonal axises of typing systems. This organization is mostly my own, but I’m freely stealing the best ideas from the papers I’ve read as well.

Typed vs Untyped - Typing is an organization of data which classifies fields or records into distinct groups. These groups can be either predefined or user defined. A language is typed if there exists a feature which helps to express such typing or if such a distinction is implicit in the language specification. Note that a language does not need to check or enforce this semantic organization in any way to be typed.

By this definition, even something like assembly language is typed for some aspects. On most ISAs, there are distinct instructions for operating on floating point numbers and integers. On the other hand, not all ISAs define separate instructions for operating on signed vs unsigned integers. This highlights the important point that a language can be typed with respect to some attribute and not typed with respect to another.

Strong vs Weak - The strength of the typing is essentially just how easy it is to get around. A strongly typed language has a type system which can not be avoided. A weakly typed system is one that is more of a suggestion than an enforced rule. Note that the strength of the typing system says nothing about when the enforcement may happen. (We’ll get to that in a second.)

In practice, every language I know of is somewhere in the middle. A language may be closer to strongly typed or more weakly typed, but it’s a matter of degree. As with typing itself, a language can also be more strongly (or weakly) typed with respect to a particular attribute.

Static vs Dynamic - Expresses when the type is checked. A statically checked language is checked at compile time. A dynamically checked language is checked at execution time. There’s been much debate as to which is preferable over the years, but the basic arguments come down to safety & performance (static) vs ease of use & expressiveness (dynamic).

The term hybrid typing is an acknowledgment that most practical languages are both statically and dynamically typed. While the terminology has only entered the academic literature in the last few years, it’s been around in practice for much longer.

Gradual typing is another recent invention that explicitly merges static and dynamic typing. The basic idea is that a program can be written in a dynamic style and moved to a fully statically checked version incrementally. In terms of real languages, the best example I’ve seen is Cython. Cython focuses on incremental performance, but incremental safety and changeability are also reasons to consider gradually typed systems. Expect a full article on gradual typing in the not too distant future; I’ve been quite engrossed with the idea.

Nominal vs Structural - Another important distinction between typing systems is how they define equivalence. A nominal system defines it by the name of a type. A structural system defines it by the actual interface and field layout of the respective types.

As a side note, the same language may define equivalence differently for varying stages of validation and/or execution. One common optimization performed in nominally typed languages is to combine the implementations of structurally equivalent types during code generation. Some languages also expose this distinction in their syntax.

A duck type system is an odd extension of a structural type system which only considers the structural equivalence at point of use. In particular, two different paths through the same function may have different required interfaces (and thus types.) You could also think of duck typing as a extreme form of dependent typing which includes conditions with runtime values.

A related classification is the rules the system defines for when substituting one type for another is legal. In short, a strictly classic type system allows no substitution, a subtype type system allows substitution along lines of inheritance, and a dependent type system allows substitution if the dependent conditions are met. Given that this is a highly complex topic which I’m not sure I fully understand yet, I plan a future post to discuss this separately. For now, don’t worry to much if this paragraph didn’t make a lot of sense.

I also plan on drilling into type conversion in detail. It has important implications both with regards to practical usability of any type system and their theoretical design.

Coding Best Practice: Make your assumptions explicit

October 19th, 2011

This is a topic I’m going to be expanding on in future posts, but for now let’s cover the basics.

Assertions are your single best tool for software reliability. Why? Unlike tests, you can write assertions which check any property of your system as it runs. Tests can only check the results of a given execution.

Assertions serve several purposes:

  1. Checking your assumptions - If you write new code that does something you don’t expect, you’ll find out on first execution that violates your assumptions. Ideally, you’ll be running your code in the debugger at the point this happens and can immediate inspect the entire state around the failure.
  2. Enforceable documentation of intent and expectations. If you’re working with a team of folks and someone uses a library in a way you didn’t expect, asserts will tell them this immediately. You should still document why your asserts are there mind you.
  3. Fault isolation. If you’ve written good assertions, your error statement will be something like “global state does not comply with expectations” right after your update function. This is much easier to debug than noticing a corrupt output thousands of operations later.
  4. Preventing corruption. If you’re using an assertion package which calls abort or triggers an exceptions, you don’t need to worry about the line following the assertion running if the assertion has been violated. This simplifies error handling immensely.
  5. Performance. Depending on your compiler, it may be able to take advantage of your assertions to optimize the code that follows. If the compiler “knows” - because you told it - that an loop iteration count must be a multiple of four, it can unroll and generate much more efficient code.

The downsides of assertions - as implemented in C with at least - are that they are extra code which executes at runtime. Some of your assertions will be pruned by the compiler, but most will remain. As such, if you add an assertion in the “wrong” spot - for example inside a tight loop - you can slow your program down quite a bit.

Before you panic, remember a few classic quotes about optimization:

  • “More computing sins are committed in the name of efficiency (without necessarily achieving it) than for any other single reason — including blind stupidity.” — W.A. Wulf
  • “We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%. A good programmer will not be lulled into complacency by such reasoning, he will be wise to look carefully at the critical code; but only after that code has been identified“[5] — Donald Knuth
  • “Bottlenecks occur in surprising places, so don’t try to second guess and put in a speed hack until you have proven that’s where the bottleneck is.” — Rob Pike

(Credit: Wikipedia, emphasis mine)

You should write your assertions, and only remove (or restructure) those that your profiler tells you are actually at issue. The tiny amount of performance you might gain by omitting them is not worth hours spent debugging or (more importantly) the lower quality software that would result.

As a side note: There’s plenty of ongoing research out there trying to either prove/disprove assertions and/or prune redundant assertions. Expect your compiler to get substantially better over the next few years about giving compiler time errors on assertion violations and pruning unnecessary assertions before runtime.

Why legacy code is important in research

October 7th, 2011

I ran across this blog entry a few weeks back and have been thinking about the value of transparency in research. After a bit of reflection, I’m going to try an experiment here. I’m posting a project idea in the “Open Proposal” vein. I’m going one step further though and posting a writeup of an idea that hasn’t even made it to proposal stage yet. Depending on how this first attempt works, I may extend it to other projects I’m working on, or I may not. We’ll see.

Please Note: This is a living document and is mostly a collection of notes and thoughts at this point. It has not been proof read, cited, fact checked, or otherwise made ready for public consumption. I’m going to be revising this periodically as I learn more about the topic and flesh out some ideas. I welcome feedback, but please don’t take any of the material in it too seriously just yet.

I can be reached for comment at public@pr.com. (Where pr.com is my personal domain.)

Read the rest of this entry »

Coding Best Practice: Todo Lists

October 6th, 2011

This may seem slightly obvious, but it’s worth saying anyways. If you’re looking at code and you notice something you’d like to change or fix, make a note of it. (If you have time to fix it now, great, but you probably don’t.) You don’t need to be super formal about this - in fact I recommend against it -, but make sure you do record it.

Generally, the problem you’re trying to solve involves the following questions:
(New guy on team): What should I be working on?
(Your boss): What do we need to do before the release?
(You): What can I do to kill time during this meeting?

I’m going to discuss my system below, but please don’t get too caught up in it. While you’re welcome to use it if you’d like, the important part is you find a system which works for you. I’ve known developers with radically different systems that worked for them. Any system or habit which allows you to routinely answer the above without much thought is just fine.

My personal practice is to maintain three lists:
1) Any serious bugs or security problems go into whatever bug tracker the project is using. This is only if I can quickly and easily create specific test cases which illustrate an actual problem. If you don’t have a formal bug tracker, a simple TODO file in the root directory will do. The important part is that a) others might fix them while you’re busy and 2) you remember what they are when its time to do a release.
2) Ugly code goes into its own list. It’s main purpose is to shame me into refactoring when the list gets really long.
3) Unconfirmed bugs go into their own list so I can make sure I make time later to either fix them, or move them into list 1. I often do not take the time to investigate them immediate after noticing them because I’m in midst of something else; breaking my concentration and workflow would be disruptive.

A few example “todo” items:
1) Use of sprintf in C/C++ — First, I take a look to see if this is an obvious bug which needs to get added to list 1. If it’s obviously not a bug or vulnerability, it goes in list 2 for later cleanup. Otherwise, it goes into list 3 and I move on.
2) Function with 10,000 lines of code — Obvious candidate for list 2
3) Undocumented pointer manipulation, const_cast(s), or reinterpret_casts(s) - List 3 if it takes more than a second to understand, otherwise list 2.

To get off list 3, an item must have been analyzed to the point that I’m fairly sure it’s not a real issue or have been moved into a real bug report. I generally try to keep list 3 empty. I might leave something on it for a couple of days, but that’s about it. If assuring myself of it’s innocuousness, I try to document or restructure the code to make the correctness obvious.*

* On first view, this might seem altruistic. After all, I’m out to help the next guy through the code. That’s altruistic right? Well, not really. Most of the time, I’m the poor smuck looking at it next time and it’s been long enough I’ve forgotten my conclusion the first time!

I generally don’t worry about list 2 unless I’m really bored, need a simple project to kill a few minutes during a meeting, or get really annoyed by one of it’s items. As such, it tends to be a pretty dang long list for any project I’ve been working on for more than a few days.

Patch design vs change design

September 6th, 2011

A recent email discussion over on the Clang development mailing list reminded me of a point that I consider rather important, but that seems to be missed in many technical discussions. To put it simply, the design of the patches used to submit a feature to the community do not need to (and usually should not) reflect the design and implementation of the feature itself. Or to put it another way, patch design != change design.

In good software engineering practice, the development of a new feature is a largely incremental process. Whether done on a branch or merely within a local directory, the usual process is to pick a small subset of the feature, implement it, then repeat until done. This has the side effect that unless one is careful, the resulting code can be rather messy: duplicated code, workarounds for previous versions, etc… At this point, any good developer (or at least one with a bit of time before this has to be in) goes back and does a bit of refactoring to clean things up.

A naive developer is tempted to present their patches to the community in the same way. This is wrong. Your coworkers don’t care the order in which you developed this in or how many drafts you had. What they care about is a) having the feature, and b) being able to understand and review the parts as they go in. If anything, the ability to understand your changes and be sure they aren’t going to break anything is actually more important than the feature you’ve implemented.

This may seem odd at first, but take a step back and think about it. You’re contributing to a large project with lots of moving parts. You’re adding one (probably relatively small) feature to a code base that already has hundreds if not thousands of features. If in the process of adding this new feature, something else breaks, do you think your user base is going to be happy? Even the ones clamoring for this new feature?

With this in mind, let’s consider what your goals should be for preparing a patch:
1) it should be small, localized, and obviously correct
2) it should add some bit of logic, structure, or functionality which is obviously useful
3) it should reduce the complexity of the change outstanding so that it has a better chance of meeting the first two goals

I wish I could tell you there was some perfect set of rules for doing this, but there really isn’t. This is where experience comes in. It’s a bit of an art to design “the perfect patch set”. If you’re not sure what you’re doing, talk to your peers. Most communities are more than willing to mentor someone who asks for help.

That concludes the core part of my argument. The rest of this is a collection of side notes that don’t directly relate to the core idea.
1) I’ve been saying “community” above, but the same applies to commercial software development. You should think of “pushing your change to mainline” as “publishing” the change to the community of your fellow developers, testers, and users.
2) From what I’ve observed in the open source world, the majority of patches that sit for long periods without attention don’t follow the guidelines I’ve suggested above. The ones that do tend to get pretty prompt attention. If you’ve ever complained about your patches not getting merged, you might want to think about that for a while.
3) Even if you end up submitting a single massive patch - which you really shouldn’t btw - running through the exercise of mentally splitting up the change into multiple smaller patches can still be useful. If you organize your submit commit around these smaller pieces, someone viewing the patch has a better chance at understanding it.
4) My argument is tangential to the use of centralized vs distributed version control system. While DVCS have major advantages, they actually make this problem worse if anything. By encouraging their users to cherry pick their changes, they encourage users to push back to the community in terms of the commits they themselves made.

Getting basic Linux commands running on Windows cmd

August 26th, 2011

As part of getting a new system configured today, I had to remind myself how to get commands such as “ls” or “g++” working on a Windows machine. It had been years since I did this the last time, so it took some research. :) I’m just making a note of it here, so that next time I know where to look.

Download MinGW and the “basic minsys system” (using their mingw-get installer). Once this runs, you’ll have the mysys command line and can run what you need. Next, you can (but don’t have to) modify your PATH variable to add the paths to the MinGW/bin and MySys/bin directories to the _very end of the string_. (You definitely want these being searched last. Otherwise, windows command scripts may start doing really weird things. There are several commands - find, sort, etc.. - which exist in each toolset.) This allows you to use Linux style command verbs in the command prompt (cmd.exe) on Windows.

To my knowledge, this works on Windows XP, and Windows 7. It probably works elsewhere.

Note: The porting work done in MinGW is pretty good, but not perfect. Don’t expect to be able to compile large open source projects without changes on your Windows box. If you want that, Cygwin is the closest you’ll get. That’s better, but far from perfect. Your best bet is probably a VirtualBox VM.

Thunderbird 5.0 Memory Consumption and Global Search

August 13th, 2011

I just upgraded to Thunderbird 5 (from Thunderbird 3.1) and noticed a huge regression in memory usage and CPU usage. It went from about 70MB idle consumption - which already thought was excessive - to 350-400 MB idle. Given my machine only has 2 GB - and given it’s a 32 bit Windows machine can only use 1GB of that for user programs - that was unacceptable. It was also consuming 98% of available CPU for hours on end. I was barely able to browser the web, much less do other useful work. After playing around for a while, I discovered an option under Advanced->General called Enable Global Search and Indexer. Disabling this and restarting dropped the memory down to 70MBs again.

Given the name, I suspect the memory consumption would have died down once it had finished indexing all of my mail. However, after 2 days of pegged CPU and memory, I don’t really care.

Interestingly, it looks like this feature was added in 3.1 - the version I had before upgrade and I never had issue before. Sounds like someone introduced a bug in 4.x or 5.x. If there’s a thunderbird developer reading this, please limit your indexing to around 40-50MB and 10% of _idle_ CPU please. Consuming everything available is decidedly not friendly behavior.

Setting up a subversion server on a ReadyNas NV

April 3rd, 2011

This post is a more or less step by step guide to building subversion on a ReadyNas NV. The steps described below start after I had tried to get git working. Make sure you read that first since I’m not going to describe the same steps and gotchas.

# Follow the instructions here to setup a development environment (mandatory).  The only extra piece is the install of man-db and libssl-dev
# http://www.readynas.com/?p=145
# Make sure you’ve install the right version of firmware and addons before continuing!
apt-get update

# I actually ran this _after_ the next block of commands, but you’re
# probably better running it first.  Hopefully, it’ll give you few confusing errors
# You _may_ need to run the steps described here: http://www.readynas.com/forum/viewtopic.php?f=35&t=15482
# I can’t remember if I actually did or not, but I had the link saved in my working set.
apt-get install man-db

#install the dev packages
apt-get install libc6-dev
apt-get install gcc
apt-get install gdb
apt-get install libtag1-dev
apt-get install uuid-dev 

# make sure that worked
echo “int main() { return 0;}” > /tmp/dummy.cxx
g++ /tmp/dummy.cxx
rm /tmp/dummy.cxx

# Install one last one
apt-get install libssl-dev 

# Instructions thanks to this wonderful thread:
# http://www.readynas.com/forum/viewtopic.php?f=35&t=19710
mkdir /tmp/svnbuild
cd /tmp/svnbuild
wget http://subversion.tigris.org/downloads/subversion-1.5.1.tar.gz
wget http://subversion.tigris.org/downloads/subversion-deps-1.5.1.tar.gz
tar xvzf subversion-1.5.1.tar.gz
tar xvzf subversion-deps-1.5.1.tar.gz
cd subversion-1.5.1/zlib/
./configure
make test
make install
cd ..
./configure –build=sparc-linux –enable-all-static –with-ssl –without-serf –with-zlib=/usr/local
make test
make install

# make sure you have it properly installed
svn –version

# This section is based on the ssh access for SVN documented here:
# http://svnbook.red-bean.com/en/1.2/svn.serverconfig.svnserve.html#svn.serverconfig.svnserve.sshauth

# http://www.debianhelp.co.uk/userandgroup.htm
addgroup svnusers
usermod root -G svnusers
usermod  -G svnusers

mkdir /path/to/root/of/repo
svnadmin create /path/to/root/of/repo

Update: Make sure your repository is _not_ on your root partition! The root partition is quite small and if it files up, you could crash the box in an unrecoverable way.  I recommend creating a separate share in FrontView, then placing your depot under /c/your_share_name/.

# http://www.debianhelp.co.uk/commands.htm
chown -R :svnusers /path/to/root/of/repo
chmod -R g+rw /path/to/root/of/repo

mkdir /tmp/svntesting
cd /tmp/svntesting
# I recommend you do this for every username to make sure you didn’t miss something above
svn list svn+ssh://username@localhost/path/to/root/of/repo

# Test an actual commit (read and write access are separate permissions)
svn co svn+ssh://username@localhost/path/to/root/of/repo working
cd working
mkdir projects
svn add projects
svn commit -m “Adding directory projects/”
rm -r /tmp/svntesting

If for some reason, the above did not work for you, here are a couple of places to start looking for ideas:
http://www.readynas.com/forum/viewtopic.php?f=35&t=19710 (fairly current. This was my starting point.)
http://blog.cumps.be/nl/Blog/Read/howto-installing-subversion-on-readynas-nv (also fairly current. Combined with the above, this gives you 90%.)
http://www.readynas.com/?p=420 (out of date, but still useful)
http://www.readynas.com/forum/viewtopic.php?f=35&t=19839&start=0&st=0&sk=t&sd=a (another approach that I did not use)

Here’s why you need to build it yourself: http://www.readynas.com/forum/viewtopic.php?t=14936

And some general subversion usage info:
http://tortoisesvn.net/docs/release/TortoiseSVN_en/tsvn-repository.html#tsvn-repository-layout
http://www.petefreitag.com/item/665.cfm

Setting up a git server on a Readynas NV

March 20th, 2011

So, over the last few days, I’ve been slowly setting up a git server to use for a personal project I’m working on. I figured I’d share a few lessons I’ve learned along the way. Please note that this is not intended to be a step-by-step tutorial. I’m writing this from memory, and I almost certainly left a piece or two out along the way.

Before doing anything else, backup your data. You can buy a 1TB external USB drive for about $100. You have no excuse for not backing up. Readynas does not clearly indicate backup progress, but you can get an idea by watching the USB volume size. If you want the backup to run in a reasonable time, make sure you disable the instant USB disconnect option. Expect your backup to run around 5 hours per 100 GB of data.

Make sure that once you complete the backup that you physically disconnect the device. The backup is mapped into the native linux filesystem. As such, you could - in theory - delete your backup if you screw up badly enough when acting as root.

Lesson 1:
You’ll need root access. Go download and install the EnableRootSSH add-on from Netgear. Your default password will be your admin password.

Lesson 2:
You’ll want apt-get. Go install the APT addon from Netgear. To pull down versions of the various packages which work for the hardware/software configuration on the box, you’ll have to modify your backports. The version of debian installed on the box (”sarge”) is quite a bit out of date and is not really supported by debian any more. I don’t have the links I used any more, but you should be able to find them on the netgear site with a bit of searching in the forums.

Lesson 3:
The newest git version that has been backported is fairly old. Some common commands like “git init –bare” do not seem to be supported. “git svn import” does not function. The first can be worked around, but the second prevents interaction with Subversion as far as I can tell.

I’m probably going to end up building git from source in the near future. If anyone wants to hear how that goes, let me know. I also ended up building subversion from source, which may fix some of the issues with “git svn”.

Lesson 4:
- You need to create users for anyone who’s going to access the server. I recommend creating the users through your Frontview web portal for safety sake. You could manually create them from your root SSH session, but I have not tried this.
- Make sure you create a home directory for them. You can do this by running the following commands:

mkdir /home/username/
chown username:users /home/username

- After creating the user, anyone you want to be able to access your repository over SSH needs to be able to login. By default, users created by Frontview do not have a shell. You’ll need to switch them from “/bin/false” to “/bin/bash” in “/etc/passwd”.
- You can confirm a valid user setup by login in via ssh (from your root session if so desired.) “ssh user@your-readynas -v”. The “-v” add verbose output for figuring out what - if anything - went wrong.