static const char rcsid[] = "$Id: utils.c,v 1.155 2005/11/15 19:49:16 will Exp $"; #include "super.h" /* Variables table flag -- !0 means has been created */ static int created_variables_table = 0; /* an expandable input buffer */ struct Ebuf { char *buf; int l; int nalloc; }; /* checksize of Ebuf; grow an Ebuf */ static char *checksize P__((struct Ebuf *cb, int N)); static char *grow P__((struct Ebuf *cb, int N)); /* The input, cleaned input, and variable expansion buffers */ static struct Ebuf ebuf = { NULL, 0, 0 }; static struct Ebuf ebuf_clean = { NULL, 0, 0 }; static struct Ebuf variablebuf = { NULL, 0, 0 }; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Get environment variable */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ char * Getenv(s) char *s; { /* Like getenv(), but returns ptr to the in "name=xxxx", * not just the xxxx. */ char **envp; int l; extern char **environ; if (!s) return (char *) NULL; l = strlen(s); for (envp=environ; *envp ; envp++) if (strncmp(*envp, s, l) == 0 && *(*envp+l) == '=') return *envp; return (char *) NULL; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Get login directory of a user */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int getlogdir(user, buf) char *user; char *buf; { /* Gets the login directory of the named user, and puts it into buf. * The "user" argument can either be a username or a uid in text form. * If user==NULL || *user == '\0', the current user is obtained. * Best if buf is MAXPATHLEN long. * 0 is returned on success; -1 on error. */ struct passwd *pass; buf[0] = '\0'; if (user != NULL && *user != '\0') { /* Name or uid given; use getpwentry */ pass = getpwentry(1, user); } else { /* No user given; use current uid */ pass = getpwuid(userinfo.orig_uid); } if (pass == (struct passwd *) NULL) return -1; (void) strcpy(buf, pass->pw_dir); return 0; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Convert string to syslog priority code */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int read_syslogcode(s, code) char *s; int *code; { /* * Takes a string like "LOG_WARNING | LOG_AUTH", and returns the * corresponding syslog numeric value in *code. * The leading "LOG_" is optional, as is the case, and the separator * can be any combination of space, dot, and |. Thus the following * are equivalent: * LOG_WARNING | LOG_AUTH * warning.auth * Returns: * 0 on success; * -1 on error, with error message to Error(). * * In s, the valid code separators are: whitespace, '.' and/or "|". * */ char *word, *xword; char *sep = " \t|."; char *p, *new_s; if (!(new_s = strdup(s))) { return Error(0, 0, "Failed to malloc space for copy of s=<%s>\n", s); } for (p = new_s; *p; p++) { if (islower(*p)) { *p = toupper(*p); } } *code = 0; for (word = strtok(new_s, sep); word; word = strtok(NULL, sep)) { if (!word) { continue; /* ignore empty field */ } if (strncmp(word, "LOG_", 4) == 0) { xword = word + 4; /* ignore leading LOG_ */ } else { xword = word; /* there is no leading LOG_ */ } if (0) { /* placeholder */ /* * ********** * Facilities * ********** */ #ifdef LOG_AUTH } else if (strcmp(xword, "AUTH") == 0) { *code |= LOG_AUTH; #endif #ifdef LOG_AUTHPRIV } else if (strcmp(xword, "AUTHPRIV") == 0) { *code |= LOG_AUTHPRIV; #endif #ifdef LOG_CRON } else if (strcmp(xword, "CRON") == 0) { *code |= LOG_CRON; #endif #ifdef LOG_DAEMON } else if (strcmp(xword, "DAEMON") == 0) { *code |= LOG_DAEMON; #endif #ifdef LOG_KERN } else if (strcmp(xword, "KERN") == 0) { *code |= LOG_KERN; #endif #ifdef LOG_LOCAL0 } else if (strcmp(xword, "LOCAL0") == 0) { *code |= LOG_LOCAL0; #endif #ifdef LOG_LOCAL1 } else if (strcmp(xword, "LOCAL1") == 0) { *code |= LOG_LOCAL1; #endif #ifdef LOG_LOCAL2 } else if (strcmp(xword, "LOCAL2") == 0) { *code |= LOG_LOCAL2; #endif #ifdef LOG_LOCAL3 } else if (strcmp(xword, "LOCAL3") == 0) { *code |= LOG_LOCAL3; #endif #ifdef LOG_LOCAL4 } else if (strcmp(xword, "LOCAL4") == 0) { *code |= LOG_LOCAL4; #endif #ifdef LOG_LOCAL5 } else if (strcmp(xword, "LOCAL5") == 0) { *code |= LOG_LOCAL5; #endif #ifdef LOG_LOCAL6 } else if (strcmp(xword, "LOCAL6") == 0) { *code |= LOG_LOCAL6; #endif #ifdef LOG_LOCAL7 } else if (strcmp(xword, "LOCAL7") == 0) { *code |= LOG_LOCAL7; #endif #ifdef LOG_LPR } else if (strcmp(xword, "LPR") == 0) { *code |= LOG_LPR; #endif #ifdef LOG_MAIL } else if (strcmp(xword, "MAIL") == 0) { *code |= LOG_MAIL; #endif #ifdef LOG_NEWS } else if (strcmp(xword, "NEWS") == 0) { *code |= LOG_NEWS; #endif #ifdef LOG_RFS } else if (strcmp(xword, "RFS") == 0) { *code |= LOG_RFS; #endif #ifdef LOG_USER } else if (strcmp(xword, "USER") == 0) { *code |= LOG_USER; #endif #ifdef LOG_UUCP } else if (strcmp(xword, "UUCP") == 0) { *code |= LOG_UUCP; #endif /* * ****** * Levels * ****** */ #ifdef LOG_ALERT } else if (strcmp(xword, "ALERT") == 0) { *code |= LOG_ALERT; #endif #ifdef LOG_CRIT } else if (strcmp(xword, "CRIT") == 0) { *code |= LOG_CRIT; #endif #ifdef LOG_DEBUG } else if (strcmp(xword, "DEBUG") == 0) { *code |= LOG_DEBUG; #endif #ifdef LOG_EMERG } else if (strcmp(xword, "EMERG") == 0) { *code |= LOG_EMERG; #endif #ifdef LOG_ERR } else if (strcmp(xword, "ERR") == 0) { *code |= LOG_ERR; #endif #ifdef LOG_INFO } else if (strcmp(xword, "INFO") == 0) { *code |= LOG_INFO; #endif #ifdef LOG_NOTICE } else if (strcmp(xword, "NOTICE") == 0) { *code |= LOG_NOTICE; #endif #ifdef LOG_WARNING } else if (strcmp(xword, "WARNING") == 0) { *code |= LOG_WARNING; #endif } else { free(new_s); return Error(0, 0, "%t\n\tUnknown syslog code <%s>\n", word); } } free(new_s); return 0; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Copies in to out, prefixing with "^" and suffixing with "$" * if these are missing. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void anchor(in, out) char *in; char *out; { void re_anchor P__((char *, char *)); if (need_re_anchor) re_anchor(in, out); else (void) strcpy(out, in); } void re_anchor(in, out) char *in; char *out; { int i; i = (*in != '^'); if (i) out[0] = '^'; (void) strcpy(out+i, in); i = strlen(out); if (out[i-1] != '$') out[i++] = '$'; out[i] = '\0'; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Grow an expandable buffer */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static char * grow(cb, nb) struct Ebuf *cb; int nb; /* amount to grow, bytes */ { if (cb->buf) cb->buf = realloc(cb->buf, cb->nalloc += nb); else cb->buf = malloc(cb->nalloc += nb); return cb->buf; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Grow buffer if less than N bytes free */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static char * checksize(cb, N) struct Ebuf *cb; int N; { if (cb->nalloc - cb->l < N) return grow(cb, 2*N); else return cb->buf; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Check if string s1 ends with string s2 */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ char *ends(s1, s2) char *s1, *s2; /* If s1 ends with string s2, a pointer to the ending of s1 is returned; * else null */ { int l1, l2; l1 = strlen(s1); l2 = strlen(s2); if (l1 < l2) return NULL; else if (strcmp(s1+l1-l2, s2) == 0) return s1+l1-l2; else return NULL; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Copy the input buffer to outbuf (default clean_buf), * cleaning out comments and the backslash-newline-whitespace parts; the * latter become plain whitespace if the backslash is preceded by letter, * digit, or underscore; otherwise, they are deleted. * Comments are also deleted. * Return a ptr to the string in the cleaned buffer. * Returns null pointer on malloc error. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ char * clean_buf(buf, outbuf) char *buf; /* input buffer */ char *outbuf; /* output buffer; NULL means to use the * ebuf_clean buffer. If non-null, you must make * sure it's >= buf in size. */ { register char *s, *t; char inquote = '\0'; if (outbuf) { t = outbuf; } else { if (!(t = outbuf = checksize(&ebuf_clean, ebuf.nalloc))) { Error(1, 0, "%t\n\tFailed to malloc space for clean copy of input text\n"); return NULL; } } /* Copy s (input buffer) to t (clean input buffer); * delete comments, delete backslash-newline-whitespace * or replace w/ ' ' */ for (s = buf; *s; ) { /* Assert not in comment */ if (*s == '\\' && *(s+1) == '\n') { /* At continued line; skip over backslash-newline-whitespace; * make it a blank if following digit,letter,_ . */ if ( (s > buf) && (isalnum(*(s-1)) || *(s-1) == '_') ) *t++ = ' '; s += 2; while (isspace(*s)) s++; /* s has been left positioned at next character to process */ } else if (inquote) { /* In a quote */ if (*s == inquote) { /* end of quote */ inquote = '\0'; } *t++ = *s++; } else if (my_qm[*(unsigned char *)s]) { /* Begin quote */ inquote = *t++ = *s++; } else if (my_cc[*(unsigned char *)s]) { /* Begin comment; skip over until EOL */ char last_noncmt = (s > buf) ? *(s-1) : '\0'; while (*s && *s != '\n') s++; if (*s && *(s-1) == '\\') { /* At continued line. Ignore comment part, and * look at last non-comment char before * backslash-newline-whitespace; then process as above. */ if ( (s > buf) && (isalnum(last_noncmt) || last_noncmt == '_') ) *t++ = ' '; s++; while (isspace(*s)) s++; } /* s has been left positioned at next character to process */ } else { /* Ordinary character */ *t++ = *s++; /* s has been left positioned at next character to process */ } } *t = '\0'; return outbuf; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Do fgets to get one logical line: join lines that are terminated * with backslash-newline. Don't discard backslash or newline (so that * we can print the exact text, if desired). * The result is stored in "ebuf" and a pointer to the string * is returned. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ char * fillbuffer(fp, all_indented, nl) FILE *fp; int *all_indented; /* Returned !0 if continued lines are all indented */ int *nl; /* Returned with number of lines read in */ { char c, *s; ebuf.l = 0; /* clear the extensible buffer */ /* Collect lines until we have a non-zero buffer (which happens with * the first line, of course) and it isn't terminated "\\\n". */ if (nl) *nl = 0; *all_indented = 1; UNTIL(ebuf.l && !(s=ends(ebuf.buf, "\\\n"))) { if (!checksize(&ebuf, 1024)) { /* Needed to, but couldn't increase the allocated space */ return NULL; } if (!fgets(ebuf.buf+ebuf.l, ebuf.nalloc - ebuf.l, fp)) return NULL; c = *(ebuf.buf + ebuf.l) ; if (nl) (*nl)++; if (ebuf.l != 0 && !(isspace(c) || c == '#')) { /* Continued line not indented. */ *all_indented = 0; } ebuf.l += strlen(ebuf.buf+ebuf.l); } return ebuf.buf; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Open a file; chain it to the previous opened list. * Returns NULL pointer on malloc error, stat error, fopen error, * ownership error, etc. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ FileList * file_open(parent, name, allow_missing, req_uid, req_gid) FileList *parent; /* null if this is head file */ char *name; /* file to open */ int allow_missing; /* silently ignore missing files if parent != null. */ uid_t *req_uid; /* NULL if default, else ptr to required uid */ gid_t *req_gid; /* NULL if default, else ptr to required gid; * this group is also allowed write permission. */ { char *s; FileList *fl; struct stat st; struct passwd *pass; int orig_uid, euid; fl = (FileList *) malloc(sizeof(FileList)); if (!fl) { Error(0, 0, "%tFailed to malloc list member space for include file <%s>\n", name); return NULL; } if (!(fl->givenname = malloc(strlen(name)+1)) ) { Error(0, 0, "%tFailed to malloc space for filename <%s>\n", name); return NULL; } strcpy(fl->givenname, name); if (*name != '/' && parent != NULL) { /* it must be relative to the superfile directory */ int l1, l2; s = strrchr(superfile, '/'); if (s) { l1 = s + 1 - superfile; l2 = strlen(name); if (!(fl->fullname = malloc(l1+l2+1)) ) { Error(0, 0, "%tFailed to malloc space for expanded filename of <%s>\n", name); return NULL; } sprintf(fl->fullname, "%.*s%s", l1, superfile, (strncmp("./", name, 2) == 0) ? name+2 : name); } else { fl->fullname = fl->givenname; } } else { /* accept the name as is */ fl->fullname = fl->givenname; } if (debug) { if (parent == NULL) { fprintf(stderr, "\tOpening file %s\n", fl->fullname); } else { fprintf(stderr, "\tLine %d: opening include file %s\n", parent->line, fl->fullname); } } /* * Handle missing-file case. */ if (stat(fl->fullname, &st) == -1) { if (errno == ENOENT && allow_missing) { if (debug) fprintf(stderr, "\tIgnoring missing file %s\n", fl->fullname); } else { Error(1, 0, "Couldn't stat super.tab %sfile `%s': ", parent == NULL ? "" : "include ", fl->fullname); } if (fl->fullname != fl->givenname) free(fl->fullname); free(fl->givenname); free(fl); if (errno == ENOENT && allow_missing) { return parent; } else { return NULL; } } /* * Check file write permissions. */ if (st.st_mode & S_IWOTH) { /* * World-writable is never ok. */ Error(0, 0, "super.tab %sfile `%s' is world-writable. Bailing out.\n", parent == NULL ? "" : "include ", fl->fullname); if (debug || it_came_from_cmdline) { Error(0, 0, "If this weren't debug mode, super would have quit here.\n"); } else { return NULL; } } else if (!req_gid && (st.st_mode & S_IWGRP)) { /* * No group was specified, but it's group-writable! */ Error(0, 0, "super.tab %sfile `%s' is group-writable. Bailing out.\n", parent == NULL ? "" : "include ", fl->fullname); if (debug || it_came_from_cmdline) { Error(0, 0, "If this weren't debug mode, super would have quit here.\n"); } else { return NULL; } } if (!parent) { /* This is the master file. Extract its owner's name for use * in the SUPER_OWNER variable. */ pass = getpwuid(st.st_uid); if (!pass) { Error(0, 0, "%t\n\tSuper.tab file `%s' owner uid is %d, \ which has not passwd entry!\n", fl->fullname, st.st_uid); return NULL; } if (add_variable("SUPER_OWNER", pass->pw_name) == -1) return NULL; if (add_variable("SUPER_HOME", pass->pw_dir) == -1) return NULL; } /* * Check file ownership. */ euid = geteuid(); orig_uid = userinfo.orig_uid; if (req_uid && req_gid) { /* * File's uid/gid must match the required uid/gid. */ if (st.st_uid != *req_uid || st.st_gid != *req_gid) { Error(0, 0, "super.tab %sfile `%s' needs to have (uid=%d, \ gid=%d), but is uid=%d, gid=%d\n", parent == NULL ? "" : "include ", fl->fullname, *req_uid, *req_gid, st.st_uid, st.st_gid); if (debug || it_came_from_cmdline) { Error(0, 0, "If this weren't debug mode, super would quit here.\n"); } else { return NULL; } } } else if (req_uid) { /* * File's uid must match the required uid. */ if (st.st_uid != *req_uid) { Error(0, 0, "super.tab %sfile `%s' needs to have uid=%d, but has uid=%d\n", parent == NULL ? "" : "include ", fl->fullname, *req_uid, st.st_uid); if (debug || it_came_from_cmdline) { Error(0, 0, "If this weren't debug mode, super would quit here.\n"); } else { return NULL; } } } else if (req_gid) { /* * File's gid must match the required gid. */ if (st.st_gid != *req_gid) { Error(0, 0, "super.tab %sfile `%s' needs to have gid=%d, but has gid=%d\n", parent == NULL ? "" : "include ", fl->fullname, *req_gid, st.st_gid); if (debug || it_came_from_cmdline) { Error(0, 0, "If this weren't debug mode, super would quit here.\n"); } else { return NULL; } } } else if (euid == 0 || orig_uid == 0) { /* Being run by root, using default rules: the super.tab file * must be owned by root. */ if (st.st_uid != 0) { Error(0, 0, "super.tab %sfile `%s' isn't owned by root, \n\ \tbut we are being run as or by root. Bailing out.\n", parent == NULL ? "" : "include ", fl->fullname); if (debug || it_came_from_cmdline) { Error(0, 0, "If this weren't debug mode, super would have quit here.\n"); } else { return NULL; } } } else if (orig_uid != userinfo.caller.pw_uid) { /* * Real uid has changed -- this happens when the command is * like "user:somecommand", in which case the file's owner * had better be "user". * */ if (st.st_uid != orig_uid) { Error(0, 0, "super.tab %sfile `%s' isn't owned by uid %d.\n", parent == NULL ? "" : "include ", fl->fullname, orig_uid); if (debug || it_came_from_cmdline) { Error(0, 0, "If this weren't debug mode, super would quit here.\n"); } else { return NULL; } } } if ((fl->fp = fopen(fl->fullname, "r")) == NULL) { Error(1, 0, "%t\n\tCouldn't open super.tab %sfile `%s': ", parent == NULL ? "" : "include ", fl->fullname); return NULL; } fl->line = 1; fl->nl = 0; fl->prev = parent; return fl; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Close a file; return previous in list, or NULL when all done */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ FileList * file_close(curr) FileList *curr; { FileList *parent; if (!curr) return (FileList *) NULL; parent = curr->prev; if (debug) { fprintf(stderr, "\tClosing %sfile %s.\n", parent == NULL ? "" : "include ", curr->fullname); if (parent) fprintf(stderr, "\tReturning to %s file %s, line %d\n", parent->prev == NULL ? "top-level" : "include ", parent->fullname, parent->line); } fclose(curr->fp); if (curr->fullname != curr->givenname) free(curr->fullname); free(curr->givenname); free(curr); return parent; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Initialize an StrArray */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void StrInit(a) StrArray *a; { a->n = 0; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Get address of string element of StrArray */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ char * StrEltGetPtr(a, ielt) StrArray *a; /* StrArray with element of interest */ int ielt; /* Element to return */ /* Returns NULL if no such element (or if unused) */ { if (ielt >= 0 && ielt < a->n && a->str[ielt].used) return a->str[ielt].s; else return NULL; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Put string into element of StrArray. * Returns -1 on malloc/realloc failure, 0 otherwise. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int StrEltCpy(a, ielt, str) StrArray *a; /* StrArray with element to fill */ int ielt; /* Element to be filled */ char *str; /* what to put into ielt'th place; if NULL, elt is "unused". */ { int l; l = (str) ? strlen(str) : 0; if (StrNalloc(a, ielt) == -1) return -1; a->str[ielt].used = 1; if (a->str[ielt].n == 0) { a->str[ielt].s = malloc(l+1); } else if (a->str[ielt].n < l+1) { a->str[ielt].s = realloc(a->str[ielt].s, l+1); a->str[ielt].n = l+1; } if (!a->str[ielt].s) { a->str[ielt].n = 0; return -1; } if (str) { strcpy(a->str[ielt].s, str ? str : ""); } else { a->str[ielt].used = 0; } return 0; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Returns number of elements of StrArray */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int StrNElts(a) StrArray *a; { return a->n; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Return last index of in-use elements of StrArray */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int StrLastInUse(a) StrArray *a; { int i; int last; for (i=0, last = -1; i < a->n; i++) { if (a->str[i].n && a->str[i].used) last = i; } return last; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Marks all elements of StrArray as unused */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void StrEltsUnused(a) StrArray *a; { int i; for (i=0; i < a->n; i++) a->str[i].used = 0; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Makes StrArray large enough to have an ielt'th element. * Returns -1 on malloc/realloc failure, 0 otherwise. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int StrNalloc(a, ielt) StrArray *a; /* StrArray that must have an ielt'th element */ int ielt; { int i; if (ielt >= a->n) { /* Have to increase the new space */ unsigned int new_n; if (a->n == 0) { new_n = (ielt < 4) ? 4 : 2*ielt ; a->str = (CountedString *) malloc(sizeof(CountedString) * new_n); } else { new_n = 2*ielt; a->str = (CountedString *) realloc((void *) a->str, sizeof(CountedString) * new_n); } if (!a->str) return -1; for (i=a->n; i < new_n; i++) a->str[i].n = 0; a->n = new_n; } return 0; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Copy StrArray src to StrArray dst, starting at element src_ielt * in the src array, and at element dst_ielt in the dst array; * copy nelt elements. * If there are fewer than (src_ielt + nelt) elements, silently stop * copying at the end of the src array. * Returns -1 on malloc/realloc failure, 0 otherwise. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int StrBulkCpy(dst, dst_ielt, src, src_ielt, nelt) StrArray *dst; /* dstination array */ int dst_ielt; /* starting element to fill */ StrArray *src; /* sourced array */ int src_ielt; /* starting element to copy from */ int nelt; /* number of elements to copy */ { int i; char *s; if ((nelt + src_ielt) > StrNElts(src)) { /* not that many src elements */ nelt = StrNElts(src) - src_ielt; } /* make sure dst has enough space */ if (StrNalloc(dst, (dst_ielt + nelt)) != 0) { return -1; /* can't make dst sufficiently large! */ } for (i = 0; i < nelt; i++) { s = StrEltGetPtr(src, i + src_ielt); if (StrEltCpy(dst, i + dst_ielt, s) != 0) { return -1; /* copy failed */ } } return 0; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * Free everything in an ArgRangePat list. * Don't free the pointer to the head. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void ARfree( ArgRangePat *head) { ArgRangePat *arp, *next; if (!head) { return; } for (arp = head->next ; arp; arp = next) { next = arp->next; if (arp->pat) { free(arp->pat); } free(arp); } } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * Insert a new string at the head of an arglist. * Return 0 on success, -1 on malloc failure. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int ARinsert( ArgRangePat *head, int arg1, int arg2, char *pat) { ArgRangePat *arp; arp = malloc(sizeof(ArgRangePat)); if (!arp) { return -1; } arp->pat = malloc(strlen(pat)+1); if (!arp->pat) { return -1; } strcpy(arp->pat, pat); arp->arg1 = arg1; arp->arg2 = arg2; arp->next = head->next; head->next = arp; return 0; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * Walk an ArgRangePat list, finding the next pattern that applies * to the iarg'th argument. If the start'th argument applies, * return it. Stop if we reach an empty pattern. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ ArgRangePat * ARnext( ArgRangePat *start, int iarg) { ArgRangePat *arp; for (arp=start; arp; arp = arp->next) { if (!arp->pat || !arp->pat[0]) { /* Stop if pattern is empty */ return NULL; } else if (arp->arg1 <= iarg && iarg <= arp->arg2) { return arp; } } /* input was null ptr, or ran out of list */ return NULL; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Copy up to n-1 characters into "to"; then null-terminate. * Return 1 if all characters fitted into "to", else return 0. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int stringcopy(to, from, n) char *to; /* buffer to copy into */ char *from; /* string to copy */ int n; /* size of to buffer */ { int l = strlen(from); if (l >= n) { strncpy(to, from, n-1); to[n-1] = '\0'; return 0; } else { strcpy(to, from); return 1; } /* NOTREACHED */ } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Opens the logfile. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void opensuperlog() { extern FILE *error_logfile; /* to tell Error() where the log is */ close_writer(); /* in case there is already one running */ if (debug || it_came_from_cmdline) { Error(0, 0, "If this weren't debug mode, \ super would try to open as log file \"%.500s\"\n", globalinfo.log.filename); return; } if (*globalinfo.log.filename == '\0') { Error(0, 0, "opensuperlog(): logfile name is (nil)\n"); return; } globalinfo.log.fp = open_writer(globalinfo.log.user, globalinfo.log.filename, &globalinfo.log.pid); error_logfile = globalinfo.log.fp; /* ...so Error() writes here too */ return; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* In order to implement the loguser=xxx option, we (1) create a pipe, * (2) fork, in the child setuid to loguid; (3) child opens logfile; * (4) child copies from pipe to logfile. Meanwhile, we return a pointer * to a stream to the pipe as the log stream seen by the parent program. * This allows us to implement a special uid for the logfile writer, * without needing the operating system to offer saved uid's or * interprocess file-descriptor passing, etc. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ FILE * open_writer(user, filename, pid_p) char *user; char *filename; pid_t *pid_p; /* filled with pid of created logger */ { FILE *fp = NULL; int fildes[2]; pid_t child; if (pipe(fildes) == -1) { (void) Error(1, 0, "Failed to created pipe for logfile; no logging: "); return NULL; } child = fork(); if (child == -1) { (void) Error(1, 0, "Failed to create child for logfile; no logging: "); return NULL; } else if (child > 0) { /* In parent -- close read side, and aim logstream at write side */ if (pid_p) *pid_p = child; (void) close(fildes[0]); if (!(fp = fdopen(fildes[1], "w"))) { (void) Error(1, 0, "failed to fdopen logfile pipe writer; no logging: "); (void) close(fildes[1]); return NULL; } } else if (child == 0) { /* In child. Open log file and copy from pipe to log. */ FILE *input; char text[2000]; (void) close(fildes[1]); if (!(input = fdopen(fildes[0], "r"))) { (void) Error(1, 0, "failed to fdopen logfile pipe reader; no logging: "); (void) close(fildes[1]); exit(1); } if (user && *user != '\0') { stringcopy(localinfo.user, user, sizeof(localinfo.user)); *localinfo.euser = '\0'; *localinfo.egroup = '\0'; *localinfo.group = '\0'; *localinfo.u_g = '\0'; if (set_u_g() == -1) { (void) Error(1, 0, "failed to setuid %s before opening logfile; no logging: ", globalinfo.log.user); exit(1); } } if (!(fp = fopen(globalinfo.log.filename, "a"))) { if (user && *user != '\0') (void) Error(1, 0, "failed to open logfile `%s' using uid `%s': ", globalinfo.log.filename, localinfo.user); else (void) Error(1, 0, "failed to open logfile `%s': ", globalinfo.log.filename); exit(1); } while (fgets(text, sizeof(text), input)) { if (fputs(text, fp) == EOF) (void) Error(1, 0, "fputs to logfile failed: "); } (void) fclose(fp); exit(0); } return fp; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Closes the logfile stream, then calls wait(). */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void close_writer() { pid_t pid; if (!globalinfo.log.fp && globalinfo.log.pid == -1) return; if (globalinfo.log.fp) { if (fclose(globalinfo.log.fp) == EOF) Error(1, 0, "failed to close globalinfo.log.fp: "); globalinfo.log.fp = NULL; } if (globalinfo.log.pid != -1) { while ((pid = wait((int *) NULL)) > 0 && pid != globalinfo.log.pid) { Error(0, 0, "wait() surprised! close_writer() received pid %d;\n\t\ expected logger pid = %d; waiting for correct pid...\n", pid, globalinfo.log.pid); } if (pid == -1) Error(1, 0, "while waiting for logger process to exit"); globalinfo.log.pid = -1; } } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Construct a host-unique directory name: * xyz.home.caltech.edu -> prefix/xyz.home.caltech.edu/user * If hostname is empty string, file is prefix/user. * WARNING: the hostname used is that from gethostname(). * Note that this is not necessarily unique across * internet domains, since it is frequently not a * fully-qualified domain name. Therefore you should NOT * share the timestamp directory outside the local domain. * * *err is returned with an errno value, if there was an error * for which there's a relevant errno. Otherwise it's set to 0. * * If msgbuf is non-null pointer, then it's *assumed* long enough * for error message (1000 chars is enough), and error messages * are printed into it; otherwise, error messages are Error()'d. * */ /* returns NULL on error, constructed path otherwise */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ char * makedirname(prefix, hostname, file, err, msgbuf) char *prefix, *hostname, *file; int *err; char *msgbuf; { int l; char *s, str[MAXPATHLEN]; *err = 0; l = strlen(prefix) + 1 + strlen(hostname) + 1; if (l >= MAXPATHLEN) { if (msgbuf) { sprintf(msgbuf, "Can't create directory <%.500s...>: it would exceed MAXPATHLEN = %d\n", prefix, MAXPATHLEN); } else { Error(0, 0, "Can't create directory <%.500s...>: it would exceed MAXPATHLEN = %d\n", prefix, MAXPATHLEN); } return NULL; } strcpy(file, prefix); if (!*hostname) return file; #ifndef HAVE_LONG_FILE_NAMES strncpy(str, hostname, 14); str[14] = '\0'; /* in case exactly 14 chars were copied */ #else strcpy(str, hostname); #endif for (s = strrchr(str, '.'); s; *s = '\0', s = strrchr(str, '.')) { strcat(file, "/"); strcat(file, s+1); } strcat(file, "/"); strcat(file, str); return file; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Creates a directory, including any needed directories leading * to it. Returns -1 on stat/mkdir failure; 0 otherwise. * WARNING: doesn't check if final component is a directory. * * *err is returned with an errno value, if there was an error * for which there's a relevant errno. Otherwise it's set to 0. * * If msgbuf is non-null pointer, then it's *assumed* long enough * for error message (1000 chars is enough), and error messages * are printed into it (without the following perror() value, which * should be added later; otherwise, error messages are Error()'d. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int makedir(dir, err, msgbuf) char *dir; /* path with directories only. */ int *err; char *msgbuf; { static struct stat st; char *q; char path[MAXPATHLEN]; *err = errno; /* First create directories along way, if necessary */ strcpy(path, dir); for (q=path; q && *q; ) { /* skip leading slashes */ while (*q == '/') q++; /* check directory before next slash */ q = strchr(q, '/'); if (q) *q = '\0'; /* Stat directory; if missing, create it */ if (stat(path, &st) != 0) { if (errno != ENOENT) { *err = errno; if (msgbuf) { sprintf(msgbuf, "Failed to stat directory <%.500s>: ", path); } else { Error(1, 0, "Failed to stat directory <%.500s>: ", path); } return -1; } else if (mkdir(path, 0700) != 0) { *err = errno; if (msgbuf) { sprintf(msgbuf, "Failed to create directory <%.500s>: ", path); } else { Error(1, 0, "Failed to create directory <%.500s>: ", path); } return -1; } } /* Restore slash */ if (q) *q = '/'; } return 0; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Adds a variable definition. * Returns 0 on success, -1 on error. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int add_variable(varname, vardefn) char *varname; char *vardefn; { /* * Allocate space for varname, vardefn, and insert into * vars table. */ ENTRY item, *found_item; char *keyspace, *dataspace; int ln = strlen(varname); int lb = strlen(vardefn); if (!created_variables_table) { if (!s_hcreate(1000)) return Error(0, 0, "%tCouldn't allocate hash table for variable processing\n"); created_variables_table = 1; } if (debug > 1) fprintf(stderr, "entering add_variable(\"%s\",\"%s\")\n", varname, vardefn); keyspace = malloc(ln+1); dataspace = malloc(lb+1); if (!keyspace || !dataspace) { return Error(0, 0, "%tFailed to allocate space for definition of variable `%s'\n", varname); } item.key = keyspace; item.data = dataspace; strcpy(item.key, varname); strcpy(item.data, vardefn); if (debug > 1) fprintf(stderr, "Adding variable `%s' = `%s'\n", item.key, item.data); /* Delete the existing value, if any */ if ((found_item = s_hsearch(item, FIND))) { /* Assume all data allocated via malloc() */ free(found_item->data); found_item->data = NULL; } if (!s_hsearch(item, ENTER)) return Error(0, 0, "%tFailed to allocate space for hash-table entry for variable `%s'\n", varname); return 0; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Add sysinfo(2) items as variables. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int add_sysinfo_variables() { char buf[500]; /* silly gimmick: define a variable using empty buf[] instead of "", * so that gcc -Wall doesn't complain if buf isn't used in at least * one of the ifdef'd regions, below. */ buf[0] = '\0'; add_variable("SI_ARCHITECTURE", buf); #ifdef HAVE_SYSINFO #ifdef SI_ARCHITECTURE if (sysinfo(SI_ARCHITECTURE, buf, sizeof(buf)) != -1) { if (add_variable("SI_ARCHITECTURE", buf) == -1) return -1; } #endif #endif if (add_variable("SI_HOSTNAME", "") == -1) return -1; #ifdef HAVE_SYSINFO #ifdef SI_HOSTNAME if (sysinfo(SI_HOSTNAME, buf, sizeof(buf)) != -1) { if (add_variable("SI_HOSTNAME", buf) == -1) return -1; } #endif #endif if (add_variable("SI_HW_PROVIDER", "") == -1) return -1; #ifdef HAVE_SYSINFO #ifdef SI_HW_PROVIDER if (sysinfo(SI_HW_PROVIDER, buf, sizeof(buf)) != -1) { if (add_variable("SI_HW_PROVIDER", buf) == -1) return -1; } #endif #endif if (add_variable("SI_HW_SERIAL", "") == -1) return -1; #ifdef HAVE_SYSINFO #ifdef SI_HW_SERIAL if (sysinfo(SI_HW_SERIAL, buf, sizeof(buf)) != -1) { if (add_variable("SI_HW_SERIAL", buf) == -1) return -1; } #endif #endif if (add_variable("SI_MACHINE", "") == -1) return -1; #ifdef HAVE_SYSINFO #ifdef SI_MACHINE if (sysinfo(SI_MACHINE, buf, sizeof(buf)) != -1) { if (add_variable("SI_MACHINE", buf) == -1) return -1; } #endif #endif if (add_variable("SI_RELEASE", "") == -1) return -1; #ifdef HAVE_SYSINFO #ifdef SI_RELEASE if (sysinfo(SI_RELEASE, buf, sizeof(buf)) != -1) { if (add_variable("SI_RELEASE", buf) == -1) return -1; } #endif #endif if (add_variable("SI_VERSION", "") == -1) return -1; #ifdef HAVE_SYSINFO #ifdef SI_VERSION if (sysinfo(SI_VERSION, buf, sizeof(buf)) != -1) { if (add_variable("SI_VERSION", buf) == -1) return -1; } #endif #endif if (add_variable("SI_SRPC_DOMAIN", "") == -1) return -1; #ifdef HAVE_SYSINFO #ifdef SI_SRPC_DOMAIN if (sysinfo(SI_SRPC_DOMAIN, buf, sizeof(buf)) != -1) { if (add_variable("SI_SRPC_DOMAIN", buf) == -1) return -1; } #endif #endif if (add_variable("SI_SYSNAME", "") == -1) return -1; #ifdef HAVE_SYSINFO #ifdef SI_SYSNAME if (sysinfo(SI_SYSNAME, buf, sizeof(buf)) != -1) { if (add_variable("SI_SYSNAME", buf) == -1) return -1; } #endif #endif return 0; } #ifdef HAVE_UNAME /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Add uname(2) items as variables. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int add_uname_variables() { struct utsname uts; if (uname(&uts) != -1) { if (add_variable("UNAME_SYSNAME", uts.sysname) || add_variable("UNAME_NODENAME", uts.nodename) || add_variable("UNAME_RELEASE", uts.release) || add_variable("UNAME_VERSION", uts.version) || add_variable("UNAME_MACHINE", uts.machine)) return -1; } else { if (add_variable("UNAME_SYSNAME", "") || add_variable("UNAME_NODENAME", "") || add_variable("UNAME_RELEASE", "") || add_variable("UNAME_VERSION", "") || add_variable("UNAME_MACHINE", "")) return -1; } return 0; } #endif /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Does variable substitution on a string. The result is stored in * an internal, reused buffer, and a pointer to that buffer is returned. * The caller must copy the returned string to a safe place before calling * any routine that might reuse the buffer. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ char * do_variables(string) char *string; { char c, *dollar, *varname; char *tail=NULL; char savechar; char key[2]; int l; ENTRY wanted_item, *found_item; dollar = strchr(string, '$'); /* Special early return for no vars */ if (!dollar) return string; if (debug > 1) fprintf(stderr, "Applying variable expansion to: `%s'\n", string); if (!created_variables_table) { Error(0, 0, "%tNo variables have been defined, but super.tab file is using some anyway!\n"); return NULL; } variablebuf.l = 0; /* Clear the buffer */ /* Initialize the buffer to be at least the same size as the input buffer */ if (!checksize(&variablebuf, ebuf.l)) { Error(1, 0, "%tCouldn't increase space for variable processing\n"); return NULL; } while (string && *string) { /* First, copy up to variable character */ dollar = strchr(string, '$'); if (!dollar) l = strlen(string); else l = dollar - string; strncpy(variablebuf.buf + variablebuf.l, string, l); variablebuf.l += l; variablebuf.buf[variablebuf.l] = '\0'; string += l; /* Reached end of string? */ if (!*string) break; /* Not at string end; expand variable */ if ( (c = *(dollar+1)) == '$') { /* $$ is a special macro name */ key[0] = c; key[1] = '\0'; varname = key; string += 2; savechar = '\0'; } else if (isalnum(c) || c == '_') { /* Replace $name -> variable defn */ for (tail=dollar+2; isalnum(*tail) || *tail == '_'; tail++) ; varname = dollar+1; /* tail points to one past last char in variable name. * Save this character, then overwrite with null char. */ savechar = *tail; *tail = '\0'; string = tail; } else if (c == '(') { /* Replace $(name) -> variable defn */ for (tail=dollar+2; isalnum(*tail) || *tail == '_'; tail++) ; /* must have reached right paren, and must make sure first * character was alphabetic */ if (tail == dollar+2) { Error(0, 0, "%tEmpty variable name is illegal: `$()'\n"); return NULL; } else if (*tail != ')' || !isalpha(*(dollar+2))) { Error(0, 0, "%tNot a valid variable name: `%.*s'\n", tail+1-dollar, dollar); return NULL; } savechar = '\0'; *tail = '\0'; varname = dollar+2; string = tail+1; } else { /* All other $X is error */ Error(0, 0, "%tIllegal variable name `$%c'. \n\ (Use `$$' to get a plain `$' passed on to the rest of file parsing.)\n", c); return NULL; } if (debug > 1) { fprintf(stderr, "Variable name is `%s'\n", varname); } if (varname) { /* Have a variable requiring expansion */ wanted_item.key = varname; found_item = s_hsearch(wanted_item, FIND); if (!found_item) { Error(0, 0, "%t\n\tNo such variable as `$%s'\n", wanted_item.key); return NULL; } l = strlen(found_item->data); if (!checksize(&variablebuf, ROUNDUP(l+1, 1024))) { Error(1, 0, "%tCouldn't increase space for variable processing\n"); return NULL; } strcpy(variablebuf.buf+variablebuf.l, found_item->data); variablebuf.l += l; variablebuf.buf[variablebuf.l] = '\0'; /* Restore the saved character */ if (savechar && tail) *tail = savechar; } } if (debug > 1) fprintf(stderr, "variable-expanded string is `%s'\n", variablebuf.buf); return variablebuf.buf; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Returns a variable's value. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ char * get_variable(varname) char *varname; { ENTRY wanted_item, *found_item; /* Have a variable requiring expansion */ wanted_item.key = varname; found_item = s_hsearch(wanted_item, FIND); if (!found_item) { return NULL; } return found_item->data; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* A function for printing one variable's name and value. * See hprint() for calling sequence. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void print_variable(indx, key, data) int indx; char *key; char *data; { /* Ignore the variable "$", which is just a cute way of getting * "$$" to translate to plain "$". */ if (strcmp(key, "$") != 0) printf("\t%s:\t%s\n", key, data); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Input is string like "lhsrhs" * If there is no , null ptr is returned. * If lhs == left, then ptr to rhs is returned; else null pointer. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ char * str_val(left, sep, str) char *left; int sep; char *str; { char *s = strchr(str, sep); if (!s /* equal sign? */ || strlen(left) != (s - str) /* not same size as `left'? */ || strncmp(left, str, s-str) != 0) /* lhs != left */ return NULL; return s+1; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* re_comp()-style interface to wildmat. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static char *shell_pattern = NULL; char * shell_compile(s) char *s; { shell_pattern = s; return NULL; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* re_exec()-style interface to wildmat. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int shell_compare(str) char *str; { extern int wildmat P__((char *str, char *pat)); int l, Reverse; char *sp; /* If the pattern is [[...]] (that is, bounded by doubled * square brackets), then we have a special case: * every character in s must match the glob pattern [...]. * For consistency with our normal shell patterns, we allow * ^[[...]] to invert the overall sense of match, but note * that the effect is the same as [[^...]]. */ Reverse = 0; sp = shell_pattern; if (*sp == '^') { Reverse = 1; sp++; } l = strlen(sp); if (sp[0] == '[' && sp[1] == '[' && sp[l-1] == ']' && sp[l-2] == ']') { /* * it's the special case... */ int last, matched, reverse; char *p; for ( ; *str; str++) { p = sp+1; /* skip leading "[" of "[[" */ if (p[1] == '^') { reverse = !Reverse; /* Inverted character class. */ p++; } else { reverse = Reverse; } matched = 0; if (p[1] == ']' || p[1] == '-') { if (*++p == *str) { matched = 1; } } for (last = *p; *++p && *p != ']'; last = *p) { if (*p == '-' && p[1] != ']' ? *str <= *++p && *str >= last : *str == *p) { matched = 1; } } if (matched == reverse) { return 0; /* fail */ } } return 1; } else { return wildmat(str, shell_pattern); } } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void init_strqtokS() { unsigned char *p; memset(my_qm, '\0', sizeof(my_qm)); for (p=(unsigned char *) QM; *p; ) my_qm[*p++] = 1; memset(my_cc, '\0', sizeof(my_cc)); for (p=(unsigned char *) CM; *p; ) my_cc[*p++] = 1; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Finds the hostname, like gethostname(), then uses canonicalize_hostname * to change it to a canonical form. * Returns 0 on success, and -1 on error, like gethostname(). */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int get_canonical_hostname(buf, len) char *buf; int len; { if (gethostname(buf, len) < 0) return Error(0, 0, "gethostname(\"%s\", %d) failed\n", buf, len); return canonicalize_hostname(buf, len); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Canonicalizes a hostname, if available. * Returns 0 on success, and -1 on error, like gethostname(). */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int canonicalize_hostname(buf, len) char *buf; int len; { #ifdef HAVE_GETHOSTBYNAME struct hostent *he, *he_nodot, *he_dot; char dot_name[1024] = "", nodot_name[1024] = ""; if (globalinfo.gethostbyname) { if ((l=strlen(buf)) > len - 2 || l > sizeof(dot_name)-1) return Error(0, 0, "hostname %s is too long for our buffer!\n", buf); /* On systems using DNS, appending a dot should force gethostbyname() * to fully expand and return fqdn. On systems using NIS, appending a * dot will cause match failure. So we try both forms, and take the * longer match. */ he_nodot = gethostbyname(buf); if (he_nodot && he_nodot->h_name) strcpy(nodot_name, he_nodot->h_name); strcat(buf, "."); he_dot = gethostbyname(buf); if (he_dot && he_dot->h_name) strcpy(dot_name, he_dot->h_name); if (strlen(dot_name) && strlen(nodot_name)) { if ((strlen(dot_name) > len - 1) || (strlen(nodot_name) > len - 1)) return Error(0, 0, "gethostbyname() returns `%s' which is too long for our buffer!\n", buf); strcpy(buf, strlen(dot_name) > strlen(nodot_name) ? dot_name : nodot_name); } else if (strlen(dot_name)) { if (strlen(dot_name) > len - 1) return Error(0, 0, "gethostbyname() returns `%s' which is too long for our buffer!\n", buf); strcpy(buf, dot_name); } else if (strlen(nodot_name)) { if (strlen(nodot_name) > len - 1) return Error(0, 0, "gethostbyname() returns `%s' which is too long for our buffer!\n", buf); strcpy(buf, nodot_name); } else { return Error(0, 0, "host `%s' not recognized by gethostbyname().\n", buf); } /* Delete trailing dot, if still present */ if ((l=strlen(buf)) > 0 && buf[l-1] == '.') buf[l-1] = '\0'; } #endif return 0; } /* * Downcase a null-terminated string. */ void strtolower(s) char *s; { for (; *s; s++) if (isupper(*s)) *s = tolower(*s); } #ifndef HAVE_STRTOL static unsigned long digit_a2d[128] = {0}; /* Initialize the character-to-digit mapping. */ static void init_digit(void) { int c; for (c=0; c < NELEM(digit_a2d); c++) digit_a2d[c] = -1; for (c = '0' ; c <= '9'; c++) digit_a2d[c] = c - '0'; digit_a2d['a']=digit_a2d['A'] = 10; digit_a2d['b']=digit_a2d['B'] = 11; digit_a2d['c']=digit_a2d['C'] = 12; digit_a2d['d']=digit_a2d['D'] = 13; digit_a2d['e']=digit_a2d['E'] = 14; digit_a2d['f']=digit_a2d['F'] = 15; digit_a2d['g']=digit_a2d['G'] = 16; digit_a2d['h']=digit_a2d['H'] = 17; digit_a2d['i']=digit_a2d['I'] = 18; digit_a2d['j']=digit_a2d['J'] = 19; digit_a2d['k']=digit_a2d['K'] = 20; digit_a2d['l']=digit_a2d['L'] = 21; digit_a2d['m']=digit_a2d['M'] = 22; digit_a2d['n']=digit_a2d['N'] = 23; digit_a2d['o']=digit_a2d['O'] = 24; digit_a2d['p']=digit_a2d['P'] = 25; digit_a2d['q']=digit_a2d['Q'] = 26; digit_a2d['r']=digit_a2d['R'] = 27; digit_a2d['s']=digit_a2d['S'] = 28; digit_a2d['t']=digit_a2d['T'] = 29; digit_a2d['u']=digit_a2d['U'] = 30; digit_a2d['v']=digit_a2d['V'] = 31; digit_a2d['w']=digit_a2d['W'] = 32; digit_a2d['x']=digit_a2d['X'] = 33; digit_a2d['y']=digit_a2d['Y'] = 34; digit_a2d['z']=digit_a2d['Z'] = 35; } long strtol(register const char *str, char **ptr, int base) { long sign=1; long value=0; long digit; if (*digit_a2d == 0) init_digit(); /* skip leading whitespace */ while (isspace(*str)) ++str; /* Check for optional sign */ switch (*str) { case '-': sign = -1; str++; break; case '+': str++; break; } /* Determine base */ if (base == 0) { if (*str != '0') { base = 10; } else if (*++str == 'x' || *str == 'X') { base = 16; ++str; } else { base = 8; } } /* Skip 0[xX], if present */ if (base == 16 && *str == '0') if (*++str == 'x' || *str == 'X') ++str; /* Convert value */ while (*str < NELEM(digit_a2d) && (digit=digit_a2d[*str]) < base && digit != -1) { value = value*base + digit; ++str; } if (ptr) *ptr = (char *) str; return sign*value; } #endif #ifndef HAVE_MEMSET void * memset(s, c, n) void *s; int c; int n; { register int i; register unsigned char *p = (unsigned char *) s; for (i=0; i sys_nerr) { sprintf(buf, "Error %d (!)", errnum); return buf; } else { #ifdef HAVE_STRERROR return strerror(errnum); #else return sys_errlist[errnum]; #endif } #endif } #ifndef HAVE_STRDUP char * strdup(s) char *s; { char *t = (char *) malloc(strlen(s)+1); if (!t) { return NULL; } strcpy(t, s); return t; } #endif #ifndef HAVE_VPRINTF /* Very system-specific. May not work for you... */ void vfprintf(stream, format, ap) FILE *stream; char *format; va_list ap; { _doprnt(format, ap, stream); } void vsprintf(str, format, ap) char *str; char *format; va_list ap; { FILE fp; fp._cnt = 077777; fp._base = fp._ptr = str; fp._bufsiz = 0; fp._flag = _IOSTRG | _IOWRT; fp._file = 0; _doprnt(format, ap, &fp); } #ifndef HAVE_DOPRNT ERROR -- YOU HAVE NEITHER v?printf() NOR _doprnt(). YOU ARE HOSED UNTIL YOU SUPPLY ONE OF THESE. #endif #endif /***********************************************************************/ /* * my_alloc, my_realloc, my_free can be handy to add debugging info to, * when allocation errors are happening. */ void * my_malloc(size_t size) { void *p = malloc(size); printf("my_malloc(%d) -> %p\n", size, p); return p; } void * my_realloc(void *ptr, size_t size) { void *p = realloc(ptr, size); printf("my_realloc(%p, %d) -> %p\n", ptr, size, p); return p; } void my_free(void *ptr) { printf("my_free(%p)\n", ptr); free(ptr); return; }