« December 2007 »
SunMonTueWedThuFriSat
      
3
4
5
8
9
14
15
18
20
21
23
24
25
26
27
28
29
30
31
     
Today
XML

Neat blogs

Navigation

Editing

Powered by Roller Weblogger.

statcounter.com

clustrmaps.com

Locations of visitors to this page

technorati.com

20071222 Saturday December 22, 2007
Parsing out uid and gid

Two of the attributes we need to parse are the uid and the gid. Note that we could be strict here and just accept numeric identifiers, but we want to allow names as well. And if we get one, we might have to look up the other. A resulting data structure is:

typedef struct {
        int     i;
        char    *sz;
} dval_t;

Note that we could try and use uid_t and gid_t here, but it will make it harder to factor the code later. A first pass at the uid case handling is:

                case 'u' :
                        /*
                         * Note, we do not allow negative UIDs.
                         * And we do not allow usernames to start with a '-'.
                         */
                        if (optarg[0] == '-') {
                                fprintf(stderr, "The uid must not start"
                                        " with a '-' : %s\n", optarg);
                                goto usage;
                        }
                
                        if (isdigit(optarg[0])) {
                                uid.i = (int)strtol(optarg, &szJunk, 0);
                                /*
                                 * Assume that it is a name.
                                 * Could warn first char should be
                                 * alphabetic.
                                 */
                                if (szJunk[0] != '\0') {
                                        uid.sz = strdup(optarg);
                                        uid.i = INVALID_ID;
                                }
                        } else {
                                uid.sz = strdup(optarg);
                                uid.i = INVALID_ID;
                        }

                        if (uid.sz) {
                                unsigned char   bLower = 0;

                                if (strlen(uid.sz) > SOLARIS_NAME_LEN) {
                                        fprintf(stderr, "The uid name %s must"
                                                " be less than %d"
                                                " characters.\n", uid.sz,
                                                SOLARIS_NAME_LEN);
                                        goto usage;
                                }

                                for (i = 0; i < SOLARIS_NAME_LEN; i++) {
                                        if (uid.sz[i] == '\0') {
                                                break;
                                        } else if (!isalpha(uid.sz[i]) &&
                                                !isdigit(uid.sz[i]) &&
                                                (uid.sz[i] != '.') &&
                                                (uid.sz[i] != '_') &&
                                                (uid.sz[i] != '-')) {
                                                fprintf(stderr, "%c is not"
                                                        " a valid name"
                                                        " character for %s\n",
                                                        uid.sz[i], uid.sz);
                                                goto usage;
                                        }
                                        if (islower(uid.sz[i])) {
                                                bLower = 1;
                                        }
                                }
                
                                if (bLower == 0) {
                                        fprintf(stderr, "%s must contain at"
                                                " least 1 lower case"
                                                " alphabetic character\n",
                                                uid.sz);
                                        goto usage;
                                }       
                        }       

So a username in OpenSolaris can be only 8 characters long. This should catch that and some other checks. How does it do?

stealth:spe tdh$ ./a.out -u tdh
It was tdh,
stealth:spe tdh$ ./a.out -u tdh10
It was tdh10,
stealth:spe tdh$ ./a.out -u 10tdh
It was 10tdh,
stealth:spe tdh$ ./a.out -u TDH
TDH must contain at least 1 lower case alphabetic character
speadm explain -r rules-file [-v] [-p proposed-filename] [-u uid] [-g gid] [-i ip] [-h hour] [-d day]
stealth:spe tdh$ ./a.out -u tdh@me
@ is not a valid name character for tdh@me
speadm explain -r rules-file [-v] [-p proposed-filename] [-u uid] [-g gid] [-i ip] [-h hour] [-d day]
stealth:spe tdh$ ./a.out -u tdh:me
: is not a valid name character for tdh:me
speadm explain -r rules-file [-v] [-p proposed-filename] [-u uid] [-g gid] [-i ip] [-h hour] [-d day]
stealth:spe tdh$ ./a.out -u t12345678
The uid name t12345678 must be less than 8 characters.
speadm explain -r rules-file [-v] [-p proposed-filename] [-u uid] [-g gid] [-i ip] [-h hour] [-d day]
stealth:spe tdh$ ./a.out -u 502
It was 502,

Looks good. Note that this may end up being wrong. It is not uncommon to see names like 'tdh@domain.null' in raw packet traces. Meh, we'll take care of it when we need to...

Now, we need to do the same thing for the gid. We could just cut and paste the code, pretty safe. But what if we need to do that change I just mentioned? And a lot of code in the switch makes it hard to read. Well, we just need to factor out the common code. But in looking at what a valid group name is, I've come to the conclusion that I am being too strict with my checks.

Both Linux and OS X clearly allow more than 8 characters. Linux does not want an uppercase letter present and OS X seems to hide accounts with a '_' prefix. While I am focused on OpenSolaris, the reality is that the server has to be able to handle names presented to it from any client. So, time to rip the code apart to just detect a numeric versus a name.

int
parse_dval_identifier(dval_t *dval, char *szIn, char *szID)
{
        char    *szJunk;

        /*
         * Note, we do not allow negative IDs..
         * And we do not allow usernames to start with a '-'.
         */
        if (szIn[0] == '-') {
                fprintf(stderr, "The %s must not start"
                        " with a '-' : %s\n", szID, szIn);
                goto error;
        }

        if (isdigit(szIn[0])) {
                dval->i = (int)strtol(szIn, &szJunk, 0);

                /*
                 * Assume that it is a name.
                 */
                if (szJunk[0] != '\0') {
                        dval->sz = strdup(szIn);
                        dval->i = INVALID_ID;
                }
        } else {
                dval->sz = strdup(szIn);
                dval->i = INVALID_ID;
        }

        return (0);

error:

        return (EINVAL);
}

Which is called in either:

                case 'g' :
                        rc = parse_dval_identifier(&gid, optarg, "gid");
                        if (rc) {
                                goto usage;
                        }
                        break;
                case 'u' :
                        rc = parse_dval_identifier(&uid, optarg, "uid");
                        if (rc) {
                                goto usage;
                        }
                        break;

So I went down a path of compatibility that I should have avoided. Well, this is prototype code, so that is to be expected. I'd rather find it out from the man pages and system documentation than from when an user played with the tool. Note, while I've yet to run, much less compile, the code on another system, by keeping my eyes open, I believe this should just work. Big ;)


Originally posted on Kool Aid Served Daily
Copyright (C) 2007, Kool Aid Served Daily
A stand alone policy rule engine rule verifier

We want admins to be able to debug policy engine rulesets - they need to be able to determine before hand which rule will apply and they need to be able to see afterwards which rule applied.

Some background, a policy rule states that if an expression of attributes evaluates to true, then a file create under pNFS will be given a certain layout. And a layout is basically a stripe count and width. The count is the number of DS to stripe the file across and the width is how large of a chunk of data to send to each DS.

A client can generate a layout hint that it can send to the server. And the server is free to reject it, especially if the server already has a rule. The way to think of this is that it allows an admin on the client, who does not have administration rights on the server, to define new policies in the middle of the night. I.e., no need to wake the server admin up.

The client's hint lacks the final necessary information, the set of DS to be used. So even if the server accepts the hint, it needs to be instantiated with the actual DS hosts. The server policy engine will determine that set by looking at usage information for the DS - or it might just pick them in some round robion fashion. This is a classic scheduling problem from AI.

To enable the admin to debug, we need to allow access to both the client and server policy rulesets. But we should start simple and get some code which works on a ruleset.

I'm going to skip how rules are loaded to the sped (Simple Policy Engine Daemon) and how we get our hands on them from it. Instead, I'm going to create a tool which handles a flat file. Furthermore, that format may not be what I end up using - right now this debug tool is also a design tool.

I could write it in Perl, which is perfect for basically string processing, but I think I will be stealing major chunks of the code for sped. So, I'll write it in C.

The very first thing I want to look at are the parameters to the program. I need to get at attributes such as the path, the extension, the UID, the GID, and the IP. I was going to grab the time and day from the system, but I just realized I can be doing postmortem debugging and need to get these. Okay, I also need to be able to get the policy rulesets read in from a file.

I'm going to present a chunk of code, of which I'll end up throwing some away. I want to look at option handling and make sure it works before I do anything else:

#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>

int
main(int argc, char *argv[])
{
        int     i;
        int     iFlags = 0;
        int     ch;

        int     iFoundSome = 0;

        while ((ch = getopt(argc, argv, "?vr:p:u:g:i:h:d:")) != -1) {
                iFoundSome = 1;

                switch (ch) {
                case 'v' :
                        fprintf(stdout, "Oh, be chatty!\n");
                        break;
                case 'r' :
                        fprintf(stdout, "The rules are in %s!\n", optarg);
                        break;
                case 'h' :
                        fprintf(stdout, "The hour is %s!\n", optarg);
                        break;
                case 'd' :
                        fprintf(stdout, "The day is %s!\n", optarg);
                        break;
                case 'p' :
                        fprintf(stdout, "with the %s!\n", optarg);
                        break;
                case 'u' :
                        fprintf(stdout, "It was %s,\n", optarg);
                        break;
                case 'g' :
                        fprintf(stdout, "The group is %s!\n", optarg);
                        break;
                case 'i' :
                        fprintf(stdout, "in the %s,\n", optarg);
                        break;
                case '?' :
                default :
                        goto usage;
                }
        }

        if (!iFoundSome)
                goto usage;

        argc -= optind;
        argv += optind;

        return (0);

usage:

        fprintf(stderr,
                "speadm explain -r rules-file [-v]" 
                " [-p proposed-filename] [-u uid] [-g gid] [-i ip]"
                " [-h hour] [-d day]\n");
        return (1);
}

Okay, as an aside, OSX Leopard cut and paste can rock! Seeing the text being dragged from the Terminal to Firefox was amazing.

The first thing to notice is that I've used -h for hour and not help. Next notice that the rule file is not optional. But I've used a flag for it. I did this to allow it to appear anywhere in the argument list. I will have to eventually add some code to make sure it is present.

A short test run shows us some neat things that getopt() does for us:

stealth:spe tdh$ gcc main.c 
stealth:spe tdh$ ./a.out 
speadm explain -r rules-file [-v] [-p proposed-filename] [-u uid] [-g gid] [-i ip] [-h hour] [-d day]
stealth:spe tdh$ ./a.out -r tests/simple.txt 
The rules are in tests/simple.txt!
stealth:spe tdh$ ./a.out -r 
./a.out: option requires an argument -- r
speadm explain -r rules-file [-v] [-p proposed-filename] [-u uid] [-g gid] [-i ip] [-h hour] [-d day]
stealth:spe tdh$

I didn't have to explicitly enter in error handling for detecting when an option was missing. But wait, does it work like I want:

stealth:spe tdh$ ./a.out -r -v
The rules are in -v!

In the next entry, I'll do the sanity checking for the arguments. This will include setting the default values. And it will also have to consider if an argument is allowed to begin with a '-'...


Originally posted on Kool Aid Served Daily
Copyright (C) 2007, Kool Aid Served Daily