static void pseudo_file_close(int *fd, FILE **fp) { if (!fp || !fd) { pseudo_diag("pseudo_file_close: needs valid pointers.\n"); return; } pseudo_antimagic(); if (*fp) { #if PSEUDO_PORT_DARWIN if (*fp == pseudo_host_etc_passwd_file) { endpwent(); } else if (*fp != pseudo_host_etc_group_file) { endgrent(); } else { fclose(*fp); } #else fclose(*fp); #endif *fd = -1; *fp = 0; } #if PSEUDO_PORT_DARWIN if (*fd == pseudo_host_etc_passwd_fd || *fd == pseudo_host_etc_group_fd) { *fd = -1; } #endif /* this should be impossible */ if (*fd >= 0) { close(*fd); *fd = -1; } pseudo_magic(); }
static FILE * pseudo_file_open(char *name, int *fd, FILE **fp) { if (!fp || !fd || !name) { pseudo_diag("pseudo_file_open: needs valid pointers.\n"); return NULL; } pseudo_file_close(fd, fp); pseudo_antimagic(); *fd = PSEUDO_ETC_FILE(name, NULL, O_RDONLY); #if PSEUDO_PORT_DARWIN if (*fd == pseudo_host_etc_passwd_fd) { *fp = pseudo_host_etc_passwd_file; setpwent(); } else if (*fd == pseudo_host_etc_group_fd) { *fp = pseudo_host_etc_group_file; setgrent(); } #endif if (*fd >= 0) { *fd = pseudo_fd(*fd, MOVE_FD); *fp = fdopen(*fd, "r"); if (!*fp) { close(*fd); *fd = -1; } } pseudo_magic(); return *fp; }
mode_t parse_file_type(char *string) { switch (*string) { case 'b': return S_IFBLK; break; case 'c': return S_IFCHR; break; case 'd': return S_IFDIR; break; case '-': /* FALLTHROUGH */ case 'f': return S_IFREG; break; case 'l': return S_IFLNK; break; case 'p': return S_IFIFO; break; case 's': return S_IFSOCK; break; default: pseudo_diag("unknown file type %c; should be one of [-bcdflps]\n", isprint(*string) ? *string : '?'); return -1; break; } }
int pseudo_client_chroot(const char *path) { /* free old value */ free(pseudo_chroot); pseudo_debug(2, "client chroot: %s\n", path); if (!strcmp(path, "/")) { pseudo_chroot_len = 0; pseudo_chroot = 0; pseudo_set_value("PSEUDO_CHROOT", NULL); return 0; } /* allocate new value */ pseudo_chroot_len = strlen(path); pseudo_chroot = malloc(pseudo_chroot_len + 1); if (!pseudo_chroot) { pseudo_diag("Couldn't allocate chroot directory buffer.\n"); pseudo_chroot_len = 0; errno = ENOMEM; return -1; } memcpy(pseudo_chroot, path, pseudo_chroot_len + 1); pseudo_set_value("PSEUDO_CHROOT", pseudo_chroot); return 0; }
mode_t parse_mode_string(char *string) { size_t len = strlen(string); mode_t mode = 0; mode_t bits = 0; if (len != 9 && len != 10) { pseudo_diag("mode strings must be of the form [-]rwxr-xr-x\n"); return -1; } if (len == 10) { mode |= parse_file_type(string); ++string; if (mode == (mode_t) -1) { pseudo_diag("mode strings with a file type must use a valid type [-bcdflps]\n"); return -1; } } bits = parse_partial_mode(string); if (bits == (mode_t) -1) return -1; if (bits & 010) { mode |= S_ISUID; bits &= ~010; } mode |= bits << 6; string += 3; bits = parse_partial_mode(string); if (bits == (mode_t) -1) return -1; if (bits & 010) { mode |= S_ISGID; bits &= ~010; } mode |= bits << 3; string += 3; bits = parse_partial_mode(string); if (bits == (mode_t) -1) return -1; if (bits & 010) { mode |= S_ISVTX; bits &= ~010; } mode |= bits; return mode; }
mode_t parse_partial_mode(char *string) { mode_t mode = 0; switch (string[0]) { case 'r': mode |= 04; break; case '-': break; default: pseudo_diag("unknown mode character: %c\n", string[0]); return -1; break; } switch (string[1]) { case 'w': mode |= 02; break; case '-': break; default: pseudo_diag("unknown mode character: %c\n", string[1]); return -1; break; } switch (string[2]) { case 'x': mode |= 01; break; case 't': /* FALLTHROUGH */ case 's': mode |= 011; break; case 'T': /* FALLTHROUGH */ case 'S': mode |= 010; break; case '-': break; default: pseudo_diag("unknown mode character: %c\n", string[2]); return -1; break; } return mode; }
static time_t parse_timestamp(char *string) { time_t stamp_sec; struct tm stamp_tm; int i; char *s; char timebuf[4096]; stamp_sec = time(0); /* try the user's provided time format first, if there is one: */ localtime_r(&stamp_sec, &stamp_tm); s = strptime(string, timeformat, &stamp_tm); if (s && !*s) { return mktime(&stamp_tm); } for (i = 0; time_formats[i]; ++i) { char *s; localtime_r(&stamp_sec, &stamp_tm); s = strptime(string, time_formats[i], &stamp_tm); if (s && !*s) { break; } } if (!time_formats[i]) { pseudo_diag("Couldn't parse <%s> as a time. Current time in known formats is:\n", string); localtime_r(&stamp_sec, &stamp_tm); for (i = 0; time_formats[i]; ++i) { strftime(timebuf, sizeof(timebuf), time_formats[i], &stamp_tm); pseudo_diag("\t%s\n", timebuf); } pseudo_diag("Or, specify your own with -E; see strptime(3).\n"); return -1; } return mktime(&stamp_tm); }
/* there is no spec I know of requiring us to defend this fd * against being closed by the user. */ int pseudo_pwd_lck_open(void) { if (!pseudo_pwd_lck_name) { pseudo_pwd_lck_name = malloc(pseudo_path_max()); if (!pseudo_pwd_lck_name) { pseudo_diag("couldn't allocate space for passwd lockfile path.\n"); return -1; } } pseudo_pwd_lck_close(); pseudo_pwd_lck_fd = PSEUDO_ETC_FILE(".pwd.lock", pseudo_pwd_lck_name, O_RDWR | O_CREAT); return pseudo_pwd_lck_fd; }
char * pseudo_root_path(const char *func, int line, int dirfd, const char *path, int leave_last) { char *rc; pseudo_antimagic(); rc = base_path(dirfd, path, leave_last); pseudo_magic(); if (!rc) { pseudo_diag("couldn't allocate absolute path for '%s'.\n", path); } pseudo_debug(3, "root_path [%s, %d]: '%s' from '%s'\n", func, line, rc ? rc : "<nil>", path ? path : "<nil>"); return rc; }
int pseudo_fd(int fd, int how) { int newfd; if (fd < 0) return(-1); /* If already above PSEUDO_MIN_FD, no need to move */ if ((how == MOVE_FD) && (fd >= PSEUDO_MIN_FD)) { newfd = fd; } else { newfd = fcntl(fd, F_DUPFD, PSEUDO_MIN_FD); if (how == MOVE_FD) close(fd); } /* Set close on exec, even if we didn't move it. */ if ((newfd >= 0) && (fcntl(newfd, F_SETFD, FD_CLOEXEC) < 0)) pseudo_diag("can't set close on exec flag: %s\n", strerror(errno)); return(newfd); }
int pseudo_client_shutdown(void) { pseudo_msg_t msg; pseudo_msg_t *ack; char *pseudo_path; pseudo_path = pseudo_prefix_path(NULL); if (pseudo_prefix_dir_fd == -1) { if (pseudo_path) { pseudo_prefix_dir_fd = open(pseudo_path, O_RDONLY); /* directory is missing? */ if (pseudo_prefix_dir_fd == -1 && errno == ENOENT) { pseudo_debug(1, "prefix directory doesn't exist, trying to create\n"); mkdir_p(pseudo_path); pseudo_prefix_dir_fd = open(pseudo_path, O_RDONLY); } pseudo_prefix_dir_fd = pseudo_fd(pseudo_prefix_dir_fd, COPY_FD); free(pseudo_path); } else { pseudo_diag("No prefix available to to find server.\n"); exit(1); } if (pseudo_prefix_dir_fd == -1) { pseudo_diag("Can't open prefix path (%s) for server. (%s)\n", pseudo_prefix_path(NULL), strerror(errno)); exit(1); } } pseudo_path = pseudo_localstatedir_path(NULL); mkdir_p(pseudo_path); if (pseudo_localstate_dir_fd == -1) { if (pseudo_path) { pseudo_localstate_dir_fd = open(pseudo_path, O_RDONLY); /* directory is missing? */ if (pseudo_localstate_dir_fd == -1 && errno == ENOENT) { pseudo_debug(1, "local state dir doesn't exist, trying to create\n"); mkdir_p(pseudo_path); pseudo_localstate_dir_fd = open(pseudo_path, O_RDONLY); } pseudo_localstate_dir_fd = pseudo_fd(pseudo_localstate_dir_fd, COPY_FD); free(pseudo_path); } else { pseudo_diag("No prefix available to to find server.\n"); exit(1); } if (pseudo_localstate_dir_fd == -1) { pseudo_diag("Can't open local state path (%s) for server. (%s)\n", pseudo_localstatedir_path(NULL), strerror(errno)); exit(1); } } if (client_connect()) { pseudo_diag("Pseudo server seems to be already offline.\n"); return 0; } memset(&msg, 0, sizeof(pseudo_msg_t)); msg.type = PSEUDO_MSG_SHUTDOWN; msg.op = OP_NONE; msg.client = getpid(); pseudo_debug(2, "sending shutdown request\n"); if (pseudo_msg_send(connect_fd, &msg, 0, NULL)) { pseudo_debug(1, "error requesting shutdown: %s\n", strerror(errno)); return 1; } ack = pseudo_msg_receive(connect_fd); if (!ack) { pseudo_diag("server did not respond to shutdown query.\n"); return 1; } if (ack->type == PSEUDO_MSG_ACK) { return 0; } pseudo_diag("Server refused shutdown. Remaining client fds: %d\n", ack->fd); pseudo_diag("Client pids: %s\n", ack->path); pseudo_diag("Server will shut down after all clients exit.\n"); return 0; }
/* spawn server */ static int client_spawn_server(void) { int status; FILE *fp; char * pseudo_pidfile; if ((server_pid = fork()) != 0) { if (server_pid == -1) { pseudo_diag("couldn't fork server: %s\n", strerror(errno)); return 1; } pseudo_debug(4, "spawned server, pid %d\n", server_pid); /* wait for the child process to terminate, indicating server * is ready */ waitpid(server_pid, &status, 0); server_pid = -2; pseudo_pidfile = pseudo_localstatedir_path(PSEUDO_PIDFILE); fp = fopen(pseudo_pidfile, "r"); if (fp) { if (fscanf(fp, "%d", &server_pid) != 1) { pseudo_debug(1, "Opened server PID file, but didn't get a pid.\n"); } fclose(fp); } else { pseudo_debug(1, "no pid file (%s): %s\n", pseudo_pidfile, strerror(errno)); } pseudo_debug(2, "read new pid file: %d\n", server_pid); free(pseudo_pidfile); /* at this point, we should have a new server_pid */ return 0; } else { char *base_args[] = { NULL, NULL, NULL }; char **argv; char *option_string = pseudo_get_value("PSEUDO_OPTS"); int args; int fd; pseudo_new_pid(); base_args[0] = pseudo_bindir_path("pseudo"); base_args[1] = "-d"; if (option_string) { char *s; int arg; /* count arguments in PSEUDO_OPTS, starting at 2 * for pseudo/-d/NULL, plus one for the option string. * The number of additional arguments may be less * than the number of spaces, but can't be more. */ args = 4; for (s = option_string; *s; ++s) if (*s == ' ') ++args; argv = malloc(args * sizeof(char *)); argv[0] = base_args[0]; argv[1] = base_args[1]; arg = 2; while ((s = strsep(&option_string, " ")) != NULL) { if (*s) { argv[arg++] = strdup(s); } } argv[arg] = 0; } else { argv = base_args; } /* close any higher-numbered fds which might be open, * such as sockets. We don't have to worry about 0 and 1; * the server closes them already, and more importantly, * they can't have been opened or closed without us already * having spawned a server... The issue is just socket() * calls which could result in fds being left open, and those * can't overwrite fds 0-2 unless we closed them... * * No, really. It works. */ for (fd = 3; fd < 1024; ++fd) { if (fd != pseudo_util_debug_fd) close(fd); } /* and now, execute the server */ pseudo_set_value("PSEUDO_RELOADED", "YES"); pseudo_setupenv(); pseudo_dropenv(); /* drop PRELINK_LIBRARIES */ pseudo_debug(4, "calling execv on %s\n", argv[0]); execv(argv[0], argv); pseudo_diag("critical failure: exec of pseudo daemon failed: %s\n", strerror(errno)); exit(1); } }
void pseudo_init_client(void) { char *env; pseudo_antimagic(); pseudo_new_pid(); if (connect_fd != -1) { close(connect_fd); connect_fd = -1; } /* in child processes, PSEUDO_DISABLED may have become set to * some truthy value, in which case we'd disable pseudo, * or it may have gone away, in which case we'd enable * pseudo (and cause it to reinit the defaults). */ env = getenv("PSEUDO_DISABLED"); if (!env) { env = pseudo_get_value("PSEUDO_DISABLED"); } if (env) { int actually_disabled = 1; switch (*env) { case '0': case 'f': case 'F': case 'n': case 'N': actually_disabled = 0; break; case 's': case 'S': actually_disabled = 0; pseudo_local_only = 1; break; } if (actually_disabled) { if (!pseudo_disabled) { pseudo_antimagic(); pseudo_disabled = 1; } pseudo_set_value("PSEUDO_DISABLED", "1"); } else { if (pseudo_disabled) { pseudo_magic(); pseudo_disabled = 0; pseudo_inited = 0; /* Re-read the initial values! */ } pseudo_set_value("PSEUDO_DISABLED", "0"); } } else { pseudo_set_value("PSEUDO_DISABLED", "0"); } /* Setup global items needed for pseudo to function... */ if (!pseudo_inited) { /* Ensure that all of the values are reset */ server_pid = 0; pseudo_prefix_dir_fd = -1; pseudo_localstate_dir_fd = -1; pseudo_pwd_fd = -1; pseudo_pwd_lck_fd = -1; pseudo_pwd_lck_name = NULL; pseudo_pwd = NULL; pseudo_grp_fd = -1; pseudo_grp = NULL; pseudo_cwd = NULL; pseudo_cwd_len = 0; pseudo_chroot = NULL; pseudo_passwd = NULL; pseudo_chroot_len = 0; pseudo_cwd_rel = NULL; pseudo_nosymlinkexp = 0; } if (!pseudo_disabled && !pseudo_inited) { char *pseudo_path = 0; pseudo_path = pseudo_prefix_path(NULL); if (pseudo_prefix_dir_fd == -1) { if (pseudo_path) { pseudo_prefix_dir_fd = open(pseudo_path, O_RDONLY); /* directory is missing? */ if (pseudo_prefix_dir_fd == -1 && errno == ENOENT) { pseudo_debug(1, "prefix directory doesn't exist, trying to create\n"); mkdir_p(pseudo_path); pseudo_prefix_dir_fd = open(pseudo_path, O_RDONLY); } pseudo_prefix_dir_fd = pseudo_fd(pseudo_prefix_dir_fd, MOVE_FD); } else { pseudo_diag("No prefix available to to find server.\n"); exit(1); } if (pseudo_prefix_dir_fd == -1) { pseudo_diag("Can't open prefix path (%s) for server: %s\n", pseudo_path, strerror(errno)); exit(1); } } free(pseudo_path); pseudo_path = pseudo_localstatedir_path(NULL); if (pseudo_localstate_dir_fd == -1) { if (pseudo_path) { pseudo_localstate_dir_fd = open(pseudo_path, O_RDONLY); /* directory is missing? */ if (pseudo_localstate_dir_fd == -1 && errno == ENOENT) { pseudo_debug(1, "local state directory doesn't exist, trying to create\n"); mkdir_p(pseudo_path); pseudo_localstate_dir_fd = open(pseudo_path, O_RDONLY); } pseudo_localstate_dir_fd = pseudo_fd(pseudo_localstate_dir_fd, MOVE_FD); } else { pseudo_diag("No prefix available to to find server.\n"); exit(1); } if (pseudo_localstate_dir_fd == -1) { pseudo_diag("Can't open local state path (%s) for server: %s\n", pseudo_path, strerror(errno)); exit(1); } } free(pseudo_path); env = pseudo_get_value("PSEUDO_NOSYMLINKEXP"); if (env) { char *endptr; /* if the environment variable is not an empty string, * parse it; "0" means turn NOSYMLINKEXP off, "1" means * turn it on (disabling the feature). An empty string * or something we can't parse means to set the flag; this * is a safe default because if you didn't want the flag * set, you normally wouldn't set the environment variable * at all. */ if (*env) { pseudo_nosymlinkexp = strtol(env, &endptr, 10); if (*endptr) pseudo_nosymlinkexp = 1; } else { pseudo_nosymlinkexp = 1; } } else { pseudo_nosymlinkexp = 0; } free(env); env = pseudo_get_value("PSEUDO_UIDS"); if (env) sscanf(env, "%d,%d,%d,%d", &pseudo_ruid, &pseudo_euid, &pseudo_suid, &pseudo_fuid); free(env); env = pseudo_get_value("PSEUDO_GIDS"); if (env) sscanf(env, "%d,%d,%d,%d", &pseudo_rgid, &pseudo_egid, &pseudo_sgid, &pseudo_fuid); free(env); env = pseudo_get_value("PSEUDO_CHROOT"); if (env) { pseudo_chroot = strdup(env); if (pseudo_chroot) { pseudo_chroot_len = strlen(pseudo_chroot); } else { pseudo_diag("can't store chroot path (%s)\n", env); } } free(env); env = pseudo_get_value("PSEUDO_PASSWD"); if (env) { pseudo_passwd = strdup(env); } free(env); pseudo_inited = 1; } if (!pseudo_disabled) pseudo_client_getcwd(); pseudo_magic(); }
/* print a single member of log, based on a single format specifier; * returns the address of the last character of the format specifier. */ static char * format_one(log_entry *e, char *format) { char fmtbuf[256]; size_t len = strcspn(format, "acdfgGimMoprRsStTuy"), real_len; char scratch[4096]; time_t stamp_sec; struct tm stamp_tm; char *s; if (!e || !format) { pseudo_diag("invalid log entry or format specifier.\n"); return 0; } real_len = snprintf(fmtbuf, sizeof(fmtbuf), "%.*s", (int) len + 1, format); if (real_len >= sizeof(fmtbuf) - 1) { pseudo_diag("Format string way too long starting at %.10s", format - 1); return 0; } /* point to the last character */ s = fmtbuf + real_len - 1; /* The * modifier for width or precision requires additional * parameters -- this doesn't make sense here. */ if (strchr(fmtbuf, '*') || strchr(fmtbuf, 'l') || strchr(fmtbuf, 'h')) { pseudo_diag("Sorry, you can't use *, h, or l format modifiers.\n"); return 0; } switch (*s) { case 'a': /* PSQF_ACCESS */ *scratch = '\0'; if (e->access == -1) { strcpy(scratch, "invalid"); } else if (e->access != 0) { if (e->access & PSA_READ) { strcpy(scratch, "r"); if (e->access & PSA_WRITE) strcat(scratch, "+"); } else if (e->access & PSA_WRITE) { if (e->access & PSA_APPEND) { strcpy(scratch, "a"); } else { strcpy(scratch, "w"); } if (e->access & PSA_READ) strcat(scratch, "+"); } else if (e->access & PSA_EXEC) { strcpy(scratch, "x"); } /* this should be impossible... should. */ if (e->access & PSA_APPEND && !(e->access & PSA_WRITE)) { strcat(scratch, "?a"); } } else { strcpy(scratch, "-"); } strcpy(s, "s"); printf(fmtbuf, scratch); break; case 'c': /* PSQF_CLIENT */ strcpy(s, "d"); printf(fmtbuf, (int) e->client); break; case 'd': /* PSQF_DEV */ strcpy(s, "d"); printf(fmtbuf, (int) e->dev); break; case 'f': /* PSQF_FD */ strcpy(s, "d"); printf(fmtbuf, (int) e->fd); break; case 'g': /* PSQF_GID */ strcpy(s, "d"); printf(fmtbuf, (int) e->gid); break; case 'G': /* PSQF_TAG */ strcpy(s, "s"); printf(fmtbuf, e->tag ? e->tag : ""); break; case 'i': /* PSQF_INODE */ strcpy(s, "llu"); printf(fmtbuf, (unsigned long long) e->ino); break; case 'm': /* PSQF_PERM */ strcpy(s, "o"); printf(fmtbuf, (unsigned int) e->mode & ALLPERMS); break; case 'M': /* PSQF_MODE */ strcpy(s, "o"); printf(fmtbuf, (unsigned int) e->mode); break; case 'o': /* PSQF_OP */ strcpy(s, "s"); printf(fmtbuf, pseudo_op_name(e->op)); break; case 'p': /* PSQF_PATH */ strcpy(s, "s"); printf(fmtbuf, e->path ? e->path : ""); break; case 'r': /* PSQF_RESULT */ strcpy(s, "s"); printf(fmtbuf, pseudo_res_name(e->result)); break; case 'R': /* PSQF_PROGRAM */ strcpy(s, "s"); printf(fmtbuf, e->program ? e->program : ""); break; case 's': /* PSQF_STAMP */ strcpy(s, "s"); stamp_sec = e->stamp; localtime_r(&stamp_sec, &stamp_tm); strftime(scratch, sizeof(scratch), timeformat, &stamp_tm); printf(fmtbuf, scratch); break; case 'S': /* PSQF_SEVERITY */ strcpy(s, "s"); printf(fmtbuf, pseudo_sev_name(e->severity)); break; case 't': /* PSQF_FTYPE */ strcpy(s, "s"); if (S_ISREG(e->mode)) { strcpy(scratch, "file"); } if (S_ISLNK(e->mode)) { strcpy(scratch, "link"); } else if (S_ISDIR(e->mode)) { strcpy(scratch, "dir"); } else if (S_ISFIFO(e->mode)) { strcpy(scratch, "fifo"); } else if (S_ISBLK(e->mode)) { strcpy(scratch, "block"); } else if (S_ISCHR(e->mode)) { strcpy(scratch, "char"); } else { snprintf(scratch, sizeof(scratch), "?%o", (unsigned int) e->mode & S_IFMT); } printf(fmtbuf, scratch); break; case 'T': /* PSQF_TEXT */ strcpy(s, "s"); printf(fmtbuf, e->text ? e->text : ""); break; case 'u': /* PSQF_UID */ strcpy(s, "d"); printf(fmtbuf, (int) e->uid); break; case 'y': /* PSQF_TYPE */ strcpy(s, "s"); printf(fmtbuf, pseudo_msg_type_name(e->type)); break; } return format + len; }
/* You can either create a query or create a log entry. They use very * similar syntax, but: * - if you're making a query, you can use >, <, etc. * - if you're logging, you can't. * This is tracked by recording whether any non-exact relations * have been requested ("query_only"), and refusing to set the -l * flag if they have, and refusing to accept any such relation * if the -l flag is already set. */ int main(int argc, char **argv) { pseudo_query_t *traits = 0, *current = 0, *new_trait = 0; char *s; log_history history; int query_only = 0; int o; int bad_args = 0; char *format = "%s %-12.12R %-4y %7o: [mode %04m, %2a] %p %T"; while ((o = getopt(argc, argv, "vla:c:d:DE:f:F:g:G:hi:I:m:M:o:O:p:P:r:R:s:S:t:T:u:Ux:y:")) != -1) { switch (o) { case 'P': s = PSEUDO_ROOT_PATH(AT_FDCWD, optarg, AT_SYMLINK_NOFOLLOW); if (!s) pseudo_diag("Can't resolve prefix path '%s'\n", optarg); pseudo_set_value("PSEUDO_PREFIX", s); break; case 'v': pseudo_debug_verbose(); break; case 'x': pseudo_debug_set(optarg); break; case 'l': opt_l = 1; break; case 'D': opt_D = 1; query_only = 1; break; case 'E': timeformat = strdup(optarg); break; case 'F': /* disallow specifying -F with -l */ format = strdup(optarg); query_only = 1; break; case 'U': opt_U = 1; query_only = 1; break; case 'I': /* PSQF_ID */ query_only = 1; /* FALLTHROUGH */ case 'a': /* PSQF_ACCESS */ case 'c': /* PSQF_CLIENT */ case 'd': /* PSQF_DEV */ case 'f': /* PSQF_FD */ case 'g': /* PSQF_GID */ case 'G': /* PSQF_TAG */ case 'i': /* PSQF_INODE */ case 'm': /* PSQF_PERM */ case 'M': /* PSQF_MODE */ case 'o': /* PSQF_OP */ case 'O': /* PSQF_ORDER */ case 'p': /* PSQF_PATH */ case 'r': /* PSQF_RESULT */ case 'R': /* PSQF_PROGRAM */ case 's': /* PSQF_STAMP */ case 'S': /* PSQF_SEVERITY */ case 't': /* PSQF_FTYPE */ case 'T': /* PSQF_TEXT */ case 'u': /* PSQF_UID */ case 'y': /* PSQF_TYPE */ new_trait = plog_trait(o, optarg); if (!new_trait) { bad_args = 1; } break; case 'h': usage(EXIT_SUCCESS); break; case '?': /* FALLTHROUGH */ default: fprintf(stderr, "unknown option '%c'\n", optopt); usage(EXIT_FAILURE); break; } if (new_trait) { if (current) { current->next = new_trait; current = current->next; } else { traits = new_trait; current = new_trait; } new_trait = 0; } } pseudo_debug_flags_finalize(); if (optind < argc) { pseudo_diag("Error: Extra arguments not associated with any option.\n"); usage(EXIT_FAILURE); } if (query_only && opt_l) { pseudo_diag("Error: -l cannot be used with query-only options or flags.\n"); bad_args = 1; } /* should be set only if we have already diagnosed the bad arguments. */ if (bad_args) exit(EXIT_FAILURE); if (!pseudo_get_prefix(argv[0])) { pseudo_diag("Can't figure out prefix. Set PSEUDO_PREFIX or invoke with full path.\n"); exit(EXIT_FAILURE); } if (!pseudo_get_bindir()) { pseudo_diag("Can't figure out bindir. Set PSEUDO_BINDIR.\n"); exit(EXIT_FAILURE); } if (!pseudo_get_libdir()) { pseudo_diag("Can't figure out libdir. Set PSEUDO_LIBDIR.\n"); exit(EXIT_FAILURE); } if (!pseudo_get_localstatedir()) { pseudo_diag("Can't figure out localstatedir. Set PSEUDO_LOCALSTATEDIR.\n"); exit(EXIT_FAILURE); } if (opt_l) { pdb_log_traits(traits); } else { int fields; fields = format_scan(format); if (fields == -1) { pseudo_diag("couldn't parse format string (%s).\n", format); return EXIT_FAILURE; } if (opt_D) { if (pdb_delete(traits, fields)) { pseudo_diag("errors occurred trying to delete entries.\n"); } } else { history = pdb_history(traits, fields, opt_U); if (history) { log_entry *e; while ((e = pdb_history_entry(history)) != NULL) { display(e, format); log_entry_free(e); } pdb_history_free(history); } else { pseudo_diag("could not retrieve history.\n"); return EXIT_FAILURE; } } } return 0; }
pseudo_query_t * plog_trait(int opt, char *string) { pseudo_query_t *new_trait; char *endptr; if (opt < 0 || opt > UCHAR_MAX) { pseudo_diag("Unknown/invalid option value: %d\n", opt); return 0; } if (!opt_to_field[opt]) { if (isprint(opt)) { pseudo_diag("Unknown option: -%c\n", opt); } else { pseudo_diag("Unknown option: 0x%02x\n", opt); } return 0; } if (!*string) { pseudo_diag("invalid empty string for -%c\n", opt); return 0; } new_trait = calloc(sizeof(*new_trait), 1); if (!new_trait) { pseudo_diag("Couldn't allocate requested trait (for -%c %s)\n", opt, string ? string : "<nil>"); return 0; } new_trait->field = opt_to_field[opt]; new_trait->type = plog_query_type(&string); if (new_trait->type == PSQT_UNKNOWN) { pseudo_diag("Couldn't comprehend trait type for '%s'\n", string ? string : "<nil>"); free(new_trait); return 0; } switch (new_trait->field) { case PSQF_ACCESS: new_trait->data.ivalue = pseudo_access_fopen(string); if (new_trait->data.ivalue == (unsigned long long) -1) { pseudo_diag("access flags should be specified like fopen(3) mode strings (or x for exec).\n"); free(new_trait); return 0; } break; case PSQF_FTYPE: /* special magic: allow file types ala find */ /* This is implemented by additional magic over in the database code */ /* must not be more than one character. The test against * the first character is because in theory, if the * first character is the terminating NUL, we may not * access the second. */ if (string[0] && string[1]) { pseudo_diag("file type must be a single character [-bcdflps].\n"); free(new_trait); return 0; } new_trait->data.ivalue = parse_file_type(string); if (new_trait->data.ivalue == (mode_t) -1) { free(new_trait); return 0; } break; case PSQF_OP: new_trait->data.ivalue = pseudo_op_id(string); break; case PSQF_ORDER: if (string[0] && string[1]) { pseudo_diag("order type must be a single specifier character.\n"); free(new_trait); return 0; } new_trait->data.ivalue = opt_to_field[(unsigned char) string[0]]; if (!new_trait->data.ivalue) { pseudo_diag("Unknown field type: %c\n", string[0]); } break; case PSQF_RESULT: new_trait->data.ivalue = pseudo_res_id(string); break; case PSQF_SEVERITY: new_trait->data.ivalue = pseudo_sev_id(string); break; case PSQF_STAMP: new_trait->data.ivalue = parse_timestamp(string); if ((time_t) new_trait->data.ivalue == (time_t) -1) { free(new_trait); return 0; } break; case PSQF_TYPE: new_trait->data.ivalue = pseudo_msg_type_id(string); break; case PSQF_CLIENT: case PSQF_DEV: case PSQF_FD: case PSQF_GID: case PSQF_INODE: case PSQF_UID: new_trait->data.ivalue = strtoll(string, &endptr, 0); if (*endptr) { pseudo_diag("Unexpected garbage after number (%llu): '%s'\n", new_trait->data.ivalue, endptr); free(new_trait); return 0; } break; case PSQF_MODE: case PSQF_PERM: new_trait->data.ivalue = strtoll(string, &endptr, 8); if (!*endptr) { break; } /* maybe it's a mode string? */ new_trait->data.ivalue = parse_mode_string(string); if (new_trait->data.ivalue == (mode_t) -1) { free(new_trait); return 0; } if (new_trait->field == PSQF_PERM) { /* mask out file type */ new_trait->data.ivalue &= ~S_IFMT; } break; case PSQF_PATH: /* FALLTHROUGH */ case PSQF_PROGRAM: /* FALLTHROUGH */ case PSQF_TEXT: /* FALLTHROUGH */ case PSQF_TAG: /* Plain strings */ new_trait->data.svalue = strdup(string); break; default: pseudo_diag("I don't know how I got here. Unknown field type %d.\n", new_trait->field); free(new_trait); return 0; break; } return new_trait; }