Wednesday, December 3, 2014

CentOS 7 Installer Woes

When I first switched to Fedora from the Ubuntu/Debian world, it gave me such a happy feeling. The installer was the best I'd ever used. It was simple and straight forward, and yet didn't make it nearly impossible to do anything remotely outside the box (ala Ubuntu).
Disk encryption was simple to do. It wasn't some scary forbidden thing, deliberately hidden away somewhere on an alternative install image. Everything felt professional, and well thought out. Not the "work in progress" feel you get with some other distros. Despite all the "Fedora is bleeding edge" hype, I actually found it to be less buggy than Ubuntu, including LTS. And when there was a bug, the folks on Redhat's Bugzilla were usually quick to find a solution.

That was Fedora 17. But it seems like they just couldn't resist the urge to fix something that wasn't broken. If you can get past the installer, though, it's still a great system (unless you use Gnome Shell IMHO).

I really can't think of anything nice to say about the installer. You'd think they would have fixed it before the RHEL 7 release. It's pretty much impossible to install to an existing LVM on LUKS setup. You can unlock the crypt, but it can't find the LVM. Your only option is to rsync all your data to another disk, and start over.

With the direction the rest of the Linux desktop world seems to be going, I find myself liking Archlinux more and more. The installation may take a little more time, but you know exactly what's going on. And you can set up the partitions however you choose.

A GUI should make a task easier and more intuitive, not less. Its purpose is to provide an interface to the CLI that requires less spell checking and manpage reading. Not to abstract it into some clever new paradigm that only the developer understands. That's how the Fedora/RHEL installer used to be. Now it's followed Gnome Shell into the "like a tablet, but not a tablet" abyss.

Monday, June 23, 2014

Recover from failed update in CentOS/RHEL 6

NOTE: It's good idea to tee the output of all of these commands, in case you need to go back and fix something.
If a major update gets interrupted, it can leave the RPM database in a pretty upgly state.

First try to complete the update:
  yum-complete-transaction
 
If this fails, start by removing all the duplicate packages:
  package-clean --cleandupes

If that command exits with an error, manually fix any problems, by updating individual packages, etc. Then re-run. Make sure to watch the output. In particular, write down any config files moved to *.rpmsave, so you can move then back when it's done.

Restore any rpmsave files that need to be.

Now try running:
  yum-complete-transaction --skip-broken
 
There's a good chance this will fail, with a message that the transaction has been disabled.

So re-run the update now:
  yum update

Reinstall the desktop, in case package-clean removed essential packages:
yum groupinstall "Base"
yum groupinstall "Desktop"
yum groupinstall "Desktop Platform"
yum groupinstall "General Purpose Desktop"

Other things you may want to reinstall:
yum install openssh-server
yum groupinstall "Directory Client" (Remember to start sssd)
yum groupinstall "Network file system client"
yum groupinstall "NFS file server"
yum groupinstall "Development Tools"
Anything else important to you. Hopefully you kept a list after setting up the system.

Reboot the system

Reinstall proprietary drivers, as needed.

Hopefully, everything works now. Please comment, if you know of other things that should be done after re-running the update.

Thursday, May 29, 2014

When and how to use exceptions in C++

These are really just some thoughts I wrote down, about how to avoid the mess that can result from handling errors using exceptions. I'm hoping to get feedback, based on the experience of others.

I started thinking about this after reading this blog post by the creator of ZeroMQ.

A function or method should throw an exception under three circumstances:
  1. If there's something wrong with an input argument.
  2. If a required condition isn't met for running the function.
  3. An operating system error occurs, such as a failure to allocate memory, or to access a device or file. This is really a special case of #2.
Therefore, the documentation should clearly specify requirements for input arguments, and the conditions that must exist for the function to execute correctly. It should also state what exceptions will be thrown if those specifications aren't met.

Any other exceptions are the result of bugs in the function's implementation, and shouldn't be included in the documentation. For example, the function may call another function that throws an invalid_argument error. However, if the calling function has total control over the argument being passed, then that exception should never be raised.

Circumstance #3 could be re-phrased as a "system event", rather than a system error. For example, an exception could be thrown when a signal is received and handled by the process.

Saturday, May 24, 2014

Initializing a C++ container with an initializer list, without C++11

This is a common scenario when programming C++. You want to do something that your instincts tell you should be possible, and yet it doesn't work. A quick Google search turns up several answers on "Stack Overflow." However, the answer is almost invariably the same: "Just compile with C++11 enabled."

Well, if you're like me, you seldom have complete control over the restraints for the project you're working on. For example, you may be developing for RHEL 6, and are required by your consumer to use the default GCC compiler version, which is 4.4. Support for C++11 is far from complete in GCC 4.4. Or you may simply not be allowed to use C++11. After all, GCC still lists C++11 support as "experimental."

I've found this particularly frustrating when initializing C++ containers, such as maps or vectors. Pre-C++11, they have to be initialized one element at a time.This can be very inconvenient, and make code look quite messy.

For instance, you may need to keep a table of values, that's initialized at start-up, and remains constant throughout program execution. The preferred way to do this, in my opinion, is to put the table in an initializer list. This can be contained in a source file, where it's used in the declaration of a static global array. It can also be defined as a macro in a header file.  Personally, I like the second approach, because it keeps the source file from looking cluttered, especially for large tables.

But the problem is that, for C++98, this only works with arrays. The closest you're going to get is to first initialize an array with the initializer list, and then use the array to construct your C++ container.

Here's an example that stores a set of tables in a vector. The tables are used to load serial port configuration parameters from a Json file. The initializer lists for the tables are defined as a macros in a header file. LookupTable is a template class used for creating read-only one-to-one maps. Internal storage for LookupTable is handled by a std::map.

#typedef LookupTable<string, unsigned> TermiosFlagMap;

#define MAPINIT(INITLIST) { \
    TermiosFlagMap::mapping temp[] = INITLIST; \
    maplen = sizeof(temp) / sizeof(TermiosFlagMap::mapping); \
    data.push_back (TermiosFlagMap (temp, temp+maplen)); \
}

class TermiosFlagMapMaster: boost::noncopyable
{
    std::vector<TermiosFlagMap> data;
public:
    TermiosFlagMapMaster()
    {
        unsigned maplen;

        MAPINIT (INPUT_FLAG_TABLE);
        MAPINIT (OUTPUT_FLAG_TABLE);
        MAPINIT (CTRL_FLAG_TABLE);
        MAPINIT (LOCAL_FLAG_TABLE);
        MAPINIT (BIT_RATE_TABLE);
    }

    TermiosFlagMap& operator[] (SerialPort::eFlagType type)
    {
        return this->data[type];
    }
} static FLAGMAPS;
#undef MAPINIT


Note that a macro function is used to avoid having to repeat the same lines of code for each table. This deserves an explanation.

A macro can be used to enclose repetitive code that can't be made into a function or an iterative loop. By enclosing the macro definition in a code block (using curly-braces), you get similar behavior to a function, such as restricted scope and variable re-initialization.

For example, you can use it to iteratively perform an operation that requires multiple definitions of an array. Enclosing the macro in a block, causes the array to go out of scope after each iteration, thus allowing it to be redefined and initialized the next time around. The macro argument can be an initializer list, itself a macro. You couldn't do this with a function, because there'd be no way to pass the initializer list to it.

This is useful for initializing C++ containers from initializer lists, even when your compiler doesn't support C++11. You'll typically want to use arrays defined locally in a function, so that their memory will be released when the function goes out of scope. Of course, creating an array, and then copying each element to a container can be expensive. However, in this case the initialization only occurs once at program start-up, and so this isn't so important.