int main(int argc, char **argv) { int x; int opt; FILE *file1; FILE *file2; file_t *files = NULL; file_t *curfile; file_t **match = NULL; filetree_t *checktree = NULL; int filecount = 0; int progress = 0; char **oldargv; int firstrecurse; ordertype_t ordertype = ORDER_TIME; #ifndef OMIT_GETOPT_LONG static struct option long_options[] = { { "omitfirst", 0, 0, 'f' }, { "recurse", 0, 0, 'r' }, { "recursive", 0, 0, 'r' }, { "recurse:", 0, 0, 'R' }, { "recursive:", 0, 0, 'R' }, { "quiet", 0, 0, 'q' }, { "sameline", 0, 0, '1' }, { "size", 0, 0, 'S' }, { "symlinks", 0, 0, 's' }, { "hardlinks", 0, 0, 'H' }, { "relink", 0, 0, 'l' }, { "noempty", 0, 0, 'n' }, { "nohidden", 0, 0, 'A' }, { "delete", 0, 0, 'd' }, { "version", 0, 0, 'v' }, { "help", 0, 0, 'h' }, { "noprompt", 0, 0, 'N' }, { "summarize", 0, 0, 'm'}, { "summary", 0, 0, 'm' }, { "permissions", 0, 0, 'p' }, { "order", 1, 0, 'o' }, { "reverse", 0, 0, 'i' }, { 0, 0, 0, 0 } }; #define GETOPT getopt_long #else #define GETOPT getopt #endif program_name = argv[0]; oldargv = cloneargs(argc, argv); while ((opt = GETOPT(argc, argv, "frRq1SsHlnAdvhNmpo:i" #ifndef OMIT_GETOPT_LONG , long_options, NULL #endif )) != EOF) { switch (opt) { case 'f': SETFLAG(flags, F_OMITFIRST); break; case 'r': SETFLAG(flags, F_RECURSE); break; case 'R': SETFLAG(flags, F_RECURSEAFTER); break; case 'q': SETFLAG(flags, F_HIDEPROGRESS); break; case '1': SETFLAG(flags, F_DSAMELINE); break; case 'S': SETFLAG(flags, F_SHOWSIZE); break; case 's': SETFLAG(flags, F_FOLLOWLINKS); break; case 'H': SETFLAG(flags, F_CONSIDERHARDLINKS); break; case 'n': SETFLAG(flags, F_EXCLUDEEMPTY); break; case 'A': SETFLAG(flags, F_EXCLUDEHIDDEN); break; case 'd': SETFLAG(flags, F_DELETEFILES); break; case 'v': printf("fdupes %s\n", VERSION); exit(0); case 'h': help_text(); exit(1); case 'N': SETFLAG(flags, F_NOPROMPT); break; case 'm': SETFLAG(flags, F_SUMMARIZEMATCHES); break; case 'p': SETFLAG(flags, F_PERMISSIONS); break; case 'o': if (!strcasecmp("name", optarg)) { ordertype = ORDER_NAME; } else if (!strcasecmp("time", optarg)) { ordertype = ORDER_TIME; } else { errormsg("invalid value for --order: '%s'\n", optarg); exit(1); } break; case 'i': SETFLAG(flags, F_REVERSE); break; default: fprintf(stderr, "Try `fdupes --help' for more information.\n"); exit(1); } } if (optind >= argc) { errormsg("no directories specified\n"); exit(1); } if (ISFLAG(flags, F_RECURSE) && ISFLAG(flags, F_RECURSEAFTER)) { errormsg("options --recurse and --recurse: are not compatible\n"); exit(1); } if (ISFLAG(flags, F_SUMMARIZEMATCHES) && ISFLAG(flags, F_DELETEFILES)) { errormsg("options --summarize and --delete are not compatible\n"); exit(1); } if (ISFLAG(flags, F_RECURSEAFTER)) { firstrecurse = nonoptafter("--recurse:", argc, oldargv, argv, optind); if (firstrecurse == argc) firstrecurse = nonoptafter("-R", argc, oldargv, argv, optind); if (firstrecurse == argc) { errormsg("-R option must be isolated from other options\n"); exit(1); } /* F_RECURSE is not set for directories before --recurse: */ for (x = optind; x < firstrecurse; x++) filecount += grokdir(argv[x], &files); /* Set F_RECURSE for directories after --recurse: */ SETFLAG(flags, F_RECURSE); for (x = firstrecurse; x < argc; x++) filecount += grokdir(argv[x], &files); } else { for (x = optind; x < argc; x++) filecount += grokdir(argv[x], &files); } if (!files) { if (!ISFLAG(flags, F_HIDEPROGRESS)) fprintf(stderr, "\r%40s\r", " "); exit(0); } curfile = files; while (curfile) { if (!checktree) registerfile(&checktree, curfile); else match = checkmatch(&checktree, checktree, curfile); if (match != NULL) { file1 = fopen(curfile->d_name, "rb"); if (!file1) { curfile = curfile->next; continue; } file2 = fopen((*match)->d_name, "rb"); if (!file2) { fclose(file1); curfile = curfile->next; continue; } if (confirmmatch(file1, file2)) { registerpair(match, curfile, (ordertype == ORDER_TIME) ? sort_pairs_by_mtime : sort_pairs_by_filename ); /*match->hasdupes = 1; curfile->duplicates = match->duplicates; match->duplicates = curfile;*/ } fclose(file1); fclose(file2); } curfile = curfile->next; if (!ISFLAG(flags, F_HIDEPROGRESS)) { fprintf(stderr, "\rProgress [%d/%d] %d%% ", progress, filecount, (int)((float) progress / (float) filecount * 100.0)); progress++; } } if (!ISFLAG(flags, F_HIDEPROGRESS)) fprintf(stderr, "\r%40s\r", " "); if (ISFLAG(flags, F_DELETEFILES)) { if (ISFLAG(flags, F_NOPROMPT)) { deletefiles(files, 0, 0); } else { if (freopen("/dev/tty", "r", stdin) == 0) { errormsg("could not open terminal for input\n"); exit(1); } deletefiles(files, 1, stdin); } } else if (ISFLAG(flags, F_SUMMARIZEMATCHES)) summarizematches(files); else printmatches(files); while (files) { curfile = files->next; free(files->d_name); free(files->crcsignature); free(files->crcpartial); free(files); files = curfile; } for (x = 0; x < argc; x++) free(oldargv[x]); free(oldargv); purgetree(checktree); return 0; }
int main(int argc, char **argv) { int c; char *copt = NULL; srvmode_t mode = SRV_NORMAL; int rfdno = -1, wfdno = -1; diod_log_init (argv[0]); diod_conf_init (); /* config file overrides defaults */ opterr = 0; while ((c = GETOPT (argc, argv, OPTIONS, longopts)) != -1) { switch (c) { case 'c': /* --config-file PATH */ copt = optarg; break; default: break; } } diod_conf_init_config_file (copt); /* Command line overrides config file. */ optind = 0; opterr = 0; while ((c = GETOPT (argc, argv, OPTIONS, longopts)) != -1) { switch (c) { case 'f': /* --foreground */ diod_conf_set_foreground (1); break; case 'r': /* --rfdno */ mode = SRV_FILEDES; rfdno = strtoul (optarg, NULL, 10); break; case 'w': /* --wfdno */ mode = SRV_FILEDES; wfdno = strtoul (optarg, NULL, 10); break; case 'd': /* --debug MASK */ diod_conf_set_debuglevel (strtoul (optarg, NULL, 0)); break; case 'l': /* --listen HOST:PORT */ if (!diod_conf_opt_listen ()) diod_conf_clr_listen (); if (!strchr (optarg, ':')) usage (); diod_conf_add_listen (optarg); break; case 't': /* --nwthreads INT */ diod_conf_set_nwthreads (strtoul (optarg, NULL, 10)); break; case 'm': /* --maxmmap INT */ diod_conf_set_maxmmap (strtoul (optarg, NULL, 10)); break; case 'c': /* --config-file PATH */ break; case 'e': /* --export PATH */ if (!diod_conf_opt_exports ()) diod_conf_clr_exports (); diod_conf_add_exports (optarg); break; case 'E': /* --export-all */ diod_conf_set_exportall (1); break; case 'o': /* --export-ops opt,[opt,...] */ diod_conf_set_exportopts (optarg); break; case 'n': /* --no-auth */ diod_conf_set_auth_required (0); break; case 'p': /* --statfs-passthru */ diod_conf_set_statfs_passthru (0); break; case 'N': /* --no-userdb */ diod_conf_set_userdb (0); break; case 'S': /* --allsquash */ diod_conf_set_allsquash (1); break; case 'U': /* --squashuser USER */ diod_conf_set_squashuser (optarg); break; case 'u': { /* --runas-uid UID */ uid_t uid; char *end; errno = 0; uid = strtoul (optarg, &end, 10); if (errno != 0) err_exit ("error parsing --runas-uid argument"); if (*end != '\0') msg_exit ("error parsing --runas-uid argument"); diod_conf_set_runasuid (uid); break; } case 'L': /* --logdest DEST */ diod_conf_set_logdest (optarg); diod_log_set_dest (optarg); break; default: usage(); } } if (optind < argc) usage(); if (diod_conf_opt_runasuid () && diod_conf_get_allsquash ()) msg_exit ("--runas-uid and allsquash cannot be used together"); if (mode == SRV_FILEDES && (rfdno == -1 || wfdno == -1)) msg_exit ("--rfdno,wfdno must be used together"); diod_conf_validate_exports (); if (geteuid () == 0) _setrlimit (); _service_run (mode, rfdno, wfdno); diod_conf_fini (); diod_log_fini (); exit (0); }
int main(int argc, char **argv) { SESSION *session; char *host; char *port; char *path; int path_len; int sessions = 1; int ch; ssize_t len; int n; int i; char *buf; const char *parse_err; struct addrinfo *res; int aierr; const char *protocols = INET_PROTO_NAME_ALL; INET_PROTO_INFO *proto_info; /* * Fingerprint executables and core dumps. */ MAIL_VERSION_STAMP_ALLOCATE; signal(SIGPIPE, SIG_IGN); msg_vstream_init(argv[0], VSTREAM_ERR); /* * Parse JCL. */ while ((ch = GETOPT(argc, argv, "46cC:f:l:m:M:r:R:s:t:vw:")) > 0) { switch (ch) { case '4': protocols = INET_PROTO_NAME_IPV4; break; case '6': protocols = INET_PROTO_NAME_IPV6; break; case 'c': count++; break; case 'C': if ((connect_count = atoi(optarg)) <= 0) usage(argv[0]); break; case 'f': sender = optarg; break; case 'l': if ((message_length = atoi(optarg)) <= 0) usage(argv[0]); break; case 'm': if ((message_count = atoi(optarg)) <= 0) usage(argv[0]); break; case 'M': if (*optarg == '[') { if (!valid_mailhost_literal(optarg, DO_GRIPE)) msg_fatal("bad address literal: %s", optarg); } else { if (!valid_hostname(optarg, DO_GRIPE)) msg_fatal("bad hostname: %s", optarg); } var_myhostname = optarg; break; case 'r': if ((recipients = atoi(optarg)) <= 0) usage(argv[0]); break; case 'R': if (fixed_delay > 0 || (random_delay = atoi(optarg)) <= 0) usage(argv[0]); break; case 's': if ((sessions = atoi(optarg)) <= 0) usage(argv[0]); break; case 't': recipient = optarg; break; case 'v': msg_verbose++; break; case 'w': if (random_delay > 0 || (fixed_delay = atoi(optarg)) <= 0) usage(argv[0]); break; default: usage(argv[0]); } } if (argc - optind != 1) usage(argv[0]); if (random_delay > 0) srand(getpid()); /* * Translate endpoint address to internal form. */ proto_info = inet_proto_init("protocols", protocols); if (strncmp(argv[optind], "unix:", 5) == 0) { path = argv[optind] + 5; path_len = strlen(path); if (path_len >= (int) sizeof(sun.sun_path)) msg_fatal("unix-domain name too long: %s", path); memset((char *) &sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; #ifdef HAS_SUN_LEN sun.sun_len = path_len + 1; #endif memcpy(sun.sun_path, path, path_len); sa = (struct sockaddr *) & sun; sa_length = sizeof(sun); } else { if (strncmp(argv[optind], "inet:", 5) == 0) argv[optind] += 5; buf = mystrdup(argv[optind]); if ((parse_err = host_port(buf, &host, (char *) 0, &port, "628")) != 0) msg_fatal("%s: %s", argv[optind], parse_err); if ((aierr = hostname_to_sockaddr(host, port, SOCK_STREAM, &res)) != 0) msg_fatal("%s: %s", argv[optind], MAI_STRERROR(aierr)); myfree(buf); sa = (struct sockaddr *) & ss; if (res->ai_addrlen > sizeof(ss)) msg_fatal("address length %d > buffer length %d", (int) res->ai_addrlen, (int) sizeof(ss)); memcpy((char *) sa, res->ai_addr, res->ai_addrlen); sa_length = res->ai_addrlen; #ifdef HAS_SA_LEN sa->sa_len = sa_length; #endif freeaddrinfo(res); } /* * Allocate space for temporary buffer. */ buffer = vstring_alloc(100); /* * Make sure we have sender and recipient addresses. */ if (var_myhostname == 0) var_myhostname = get_hostname(); if (sender == 0 || recipient == 0) { vstring_sprintf(buffer, "foo@%s", var_myhostname); defaddr = mystrdup(vstring_str(buffer)); if (sender == 0) sender = defaddr; if (recipient == 0) recipient = defaddr; } /* * Prepare some results that may be used multiple times: the message * content netstring, the sender netstring, and the recipient netstrings. */ mydate = mail_date(time((time_t *) 0)); mypid = getpid(); message_buffer = vstring_alloc(message_length + 200); vstring_sprintf(buffer, "From: <%s>\nTo: <%s>\nDate: %s\nMessage-Id: <%d@%s>\n\n", sender, recipient, mydate, mypid, var_myhostname); for (n = 1; LEN(buffer) < message_length; n++) { for (i = 0; i < n && i < 79; i++) VSTRING_ADDCH(buffer, 'X'); VSTRING_ADDCH(buffer, '\n'); } STR(buffer)[message_length - 1] = '\n'; netstring_memcpy(message_buffer, STR(buffer), message_length); len = strlen(sender); sender_buffer = vstring_alloc(len); netstring_memcpy(sender_buffer, sender, len); if (recipients == 1) { len = strlen(recipient); recipient_buffer = vstring_alloc(len); netstring_memcpy(recipient_buffer, recipient, len); } else { recipient_buffer = vstring_alloc(100); for (n = 0; n < recipients; n++) { vstring_sprintf(buffer, "%d%s", n, recipient); netstring_memcat(recipient_buffer, STR(buffer), LEN(buffer)); } } /* * Start sessions. */ while (sessions-- > 0) { session = (SESSION *) mymalloc(sizeof(*session)); session->stream = 0; session->xfer_count = 0; session->connect_count = connect_count; session->next = 0; session_count++; startup(session); } for (;;) { event_loop(-1); if (session_count <= 0 && message_count <= 0) { if (count) { VSTREAM_PUTC('\n', VSTREAM_OUT); vstream_fflush(VSTREAM_OUT); } exit(0); } } }
int main(int argc, char **argv) { struct stat st; int fd; int c; VSTRING *buf; int status; MAIL_STREAM *dst; int rec_type; static char *segment_info[] = { REC_TYPE_POST_ENVELOPE, REC_TYPE_POST_CONTENT, REC_TYPE_POST_EXTRACT, "" }; char **expected; uid_t uid = getuid(); ARGV *import_env; const char *error_text; char *attr_name; char *attr_value; const char *errstr; char *junk; struct timeval start; int saved_errno; int from_count = 0; int rcpt_count = 0; int validate_input = 1; /* * Fingerprint executables and core dumps. */ MAIL_VERSION_STAMP_ALLOCATE; /* * Be consistent with file permissions. */ umask(022); /* * To minimize confusion, make sure that the standard file descriptors * are open before opening anything else. XXX Work around for 44BSD where * fstat can return EBADF on an open file descriptor. */ for (fd = 0; fd < 3; fd++) if (fstat(fd, &st) == -1 && (close(fd), open("/dev/null", O_RDWR, 0)) != fd) msg_fatal("open /dev/null: %m"); /* * Set up logging. Censor the process name: it is provided by the user. */ argv[0] = "postdrop"; msg_vstream_init(argv[0], VSTREAM_ERR); msg_syslog_init(mail_task("postdrop"), LOG_PID, LOG_FACILITY); set_mail_conf_str(VAR_PROCNAME, var_procname = mystrdup(argv[0])); /* * Check the Postfix library version as soon as we enable logging. */ MAIL_VERSION_CHECK; /* * Parse JCL. This program is set-gid and must sanitize all command-line * arguments. The configuration directory argument is validated by the * mail configuration read routine. Don't do complex things until we have * completed initializations. */ while ((c = GETOPT(argc, argv, "c:rv")) > 0) { switch (c) { case 'c': if (setenv(CONF_ENV_PATH, optarg, 1) < 0) msg_fatal("out of memory"); break; case 'r': /* forward compatibility */ break; case 'v': if (geteuid() == 0) msg_verbose++; break; default: msg_fatal("usage: %s [-c config_dir] [-v]", argv[0]); } } /* * Read the global configuration file and extract configuration * information. Some claim that the user should supply the working * directory instead. That might be OK, given that this command needs * write permission in a subdirectory called "maildrop". However we still * need to reliably detect incomplete input, and so we must perform * record-level I/O. With that, we should also take the opportunity to * perform some sanity checks on the input. */ mail_conf_read(); /* Re-evaluate mail_task() after reading main.cf. */ msg_syslog_init(mail_task("postdrop"), LOG_PID, LOG_FACILITY); get_mail_conf_str_table(str_table); /* * Mail submission access control. Should this be in the user-land gate, * or in the daemon process? */ mail_dict_init(); if ((errstr = check_user_acl_byuid(VAR_SUBMIT_ACL, var_submit_acl, uid)) != 0) msg_fatal("User %s(%ld) is not allowed to submit mail", errstr, (long) uid); /* * Stop run-away process accidents by limiting the queue file size. This * is not a defense against DOS attack. */ if (var_message_limit > 0 && get_file_limit() > var_message_limit) set_file_limit((off_t) var_message_limit); /* * This program is installed with setgid privileges. Strip the process * environment so that we don't have to trust the C library. */ import_env = mail_parm_split(VAR_IMPORT_ENVIRON, var_import_environ); clean_env(import_env->argv); argv_free(import_env); if (chdir(var_queue_dir)) msg_fatal("chdir %s: %m", var_queue_dir); if (msg_verbose) msg_info("chdir %s", var_queue_dir); /* * Set up signal handlers and a runtime error handler so that we can * clean up incomplete output. * * postdrop_sig() uses the in-kernel SIGINT handler address as an atomic * variable to prevent nested postdrop_sig() calls. For this reason, the * SIGINT handler must be configured before other signal handlers are * allowed to invoke postdrop_sig(). */ signal(SIGPIPE, SIG_IGN); signal(SIGXFSZ, SIG_IGN); signal(SIGINT, postdrop_sig); signal(SIGQUIT, postdrop_sig); if (signal(SIGTERM, SIG_IGN) == SIG_DFL) signal(SIGTERM, postdrop_sig); if (signal(SIGHUP, SIG_IGN) == SIG_DFL) signal(SIGHUP, postdrop_sig); msg_cleanup(postdrop_cleanup); /* End of initializations. */ /* * Don't trust the caller's time information. */ GETTIMEOFDAY(&start); /* * Create queue file. mail_stream_file() never fails. Send the queue ID * to the caller. Stash away a copy of the queue file name so we can * clean up in case of a fatal error or an interrupt. */ dst = mail_stream_file(MAIL_QUEUE_MAILDROP, MAIL_CLASS_PUBLIC, var_pickup_service, 0444); attr_print(VSTREAM_OUT, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_QUEUEID, dst->id), ATTR_TYPE_END); vstream_fflush(VSTREAM_OUT); postdrop_path = mystrdup(VSTREAM_PATH(dst->stream)); /* * Copy stdin to file. The format is checked so that we can recognize * incomplete input and cancel the operation. With the sanity checks * applied here, the pickup daemon could skip format checks and pass a * file descriptor to the cleanup daemon. These are by no means all * sanity checks - the cleanup service and queue manager services will * reject messages that lack required information. * * If something goes wrong, slurp up the input before responding to the * client, otherwise the client will give up after detecting SIGPIPE. * * Allow attribute records if the attribute specifies the MIME body type * (sendmail -B). */ vstream_control(VSTREAM_IN, CA_VSTREAM_CTL_PATH("stdin"), CA_VSTREAM_CTL_END); buf = vstring_alloc(100); expected = segment_info; /* Override time information from the untrusted caller. */ rec_fprintf(dst->stream, REC_TYPE_TIME, REC_TYPE_TIME_FORMAT, REC_TYPE_TIME_ARG(start)); for (;;) { /* Don't allow PTR records. */ rec_type = rec_get_raw(VSTREAM_IN, buf, var_line_limit, REC_FLAG_NONE); if (rec_type == REC_TYPE_EOF) { /* request cancelled */ mail_stream_cleanup(dst); if (remove(postdrop_path)) msg_warn("uid=%ld: remove %s: %m", (long) uid, postdrop_path); else if (msg_verbose) msg_info("remove %s", postdrop_path); myfree(postdrop_path); postdrop_path = 0; exit(0); } if (rec_type == REC_TYPE_ERROR) msg_fatal("uid=%ld: malformed input", (long) uid); if (strchr(*expected, rec_type) == 0) msg_fatal("uid=%ld: unexpected record type: %d", (long) uid, rec_type); if (rec_type == **expected) expected++; /* Override time information from the untrusted caller. */ if (rec_type == REC_TYPE_TIME) continue; /* Check these at submission time instead of pickup time. */ if (rec_type == REC_TYPE_FROM) from_count++; if (rec_type == REC_TYPE_RCPT) rcpt_count++; /* Limit the attribute types that users may specify. */ if (rec_type == REC_TYPE_ATTR) { if ((error_text = split_nameval(vstring_str(buf), &attr_name, &attr_value)) != 0) { msg_warn("uid=%ld: ignoring malformed record: %s: %.200s", (long) uid, error_text, vstring_str(buf)); continue; } #define STREQ(x,y) (strcmp(x,y) == 0) if ((STREQ(attr_name, MAIL_ATTR_ENCODING) && (STREQ(attr_value, MAIL_ATTR_ENC_7BIT) || STREQ(attr_value, MAIL_ATTR_ENC_8BIT) || STREQ(attr_value, MAIL_ATTR_ENC_NONE))) || STREQ(attr_name, MAIL_ATTR_DSN_ENVID) || STREQ(attr_name, MAIL_ATTR_DSN_NOTIFY) || rec_attr_map(attr_name) || (STREQ(attr_name, MAIL_ATTR_RWR_CONTEXT) && (STREQ(attr_value, MAIL_ATTR_RWR_LOCAL) || STREQ(attr_value, MAIL_ATTR_RWR_REMOTE))) || STREQ(attr_name, MAIL_ATTR_TRACE_FLAGS)) { /* XXX */ rec_fprintf(dst->stream, REC_TYPE_ATTR, "%s=%s", attr_name, attr_value); } else { msg_warn("uid=%ld: ignoring attribute record: %.200s=%.200s", (long) uid, attr_name, attr_value); } continue; } if (REC_PUT_BUF(dst->stream, rec_type, buf) < 0) { /* rec_get() errors must not clobber errno. */ saved_errno = errno; while ((rec_type = rec_get_raw(VSTREAM_IN, buf, var_line_limit, REC_FLAG_NONE)) != REC_TYPE_END && rec_type != REC_TYPE_EOF) if (rec_type == REC_TYPE_ERROR) msg_fatal("uid=%ld: malformed input", (long) uid); validate_input = 0; errno = saved_errno; break; } if (rec_type == REC_TYPE_END) break; } vstring_free(buf); /* * As of Postfix 2.7 the pickup daemon discards mail without recipients. * Such mail may enter the maildrop queue when "postsuper -r" is invoked * before the queue manager deletes an already delivered message. Looking * at file ownership is not a good way to make decisions on what mail to * discard. Instead, the pickup server now requires that new submissions * always have at least one recipient record. * * The Postfix sendmail command already rejects mail without recipients. * However, in the future postdrop may receive mail via other programs, * so we add a redundant recipient check here for future proofing. * * The test for the sender address is just for consistency of error * reporting (report at submission time instead of pickup time). Besides * the segment terminator records, there aren't any other mandatory * records in a Postfix submission queue file. */ if (validate_input && (from_count == 0 || rcpt_count == 0)) { status = CLEANUP_STAT_BAD; mail_stream_cleanup(dst); } /* * Finish the file. */ else if ((status = mail_stream_finish(dst, (VSTRING *) 0)) != 0) { msg_warn("uid=%ld: %m", (long) uid); postdrop_cleanup(); } /* * Disable deletion on fatal error before reporting success, so the file * will not be deleted after we have taken responsibility for delivery. */ if (postdrop_path) { junk = postdrop_path; postdrop_path = 0; myfree(junk); } /* * Send the completion status to the caller and terminate. */ attr_print(VSTREAM_OUT, ATTR_FLAG_NONE, SEND_ATTR_INT(MAIL_ATTR_STATUS, status), SEND_ATTR_STR(MAIL_ATTR_WHY, ""), ATTR_TYPE_END); vstream_fflush(VSTREAM_OUT); exit(status); }
int main(int argc, char ** argv1) { int ch; TSK_TCHAR *idx_type = NULL; TSK_TCHAR *db_file = NULL; TSK_TCHAR *lookup_file = NULL; unsigned int flags = 0; TSK_HDB_INFO *hdb_info; TSK_TCHAR **argv; bool create = false; bool addHash = false; #ifdef TSK_WIN32 // On Windows, get the wide arguments (mingw doesn't support wmain) argv = CommandLineToArgvW(GetCommandLineW(), &argc); if( argv == NULL) { tsk_fprintf(stderr, "Error getting wide arguments\n"); exit(1); } #else argv = (TSK_TCHAR **)argv1; #endif progname = argv[0]; setlocale(LC_ALL, ""); while ((ch = GETOPT(argc, argv, _TSK_T("cef:i:aqV"))) > 0) { switch (ch) { case _TSK_T('e'): flags |= TSK_HDB_FLAG_EXT; break; case _TSK_T('f'): lookup_file = OPTARG; break; case _TSK_T('i'): idx_type = OPTARG; break; case _TSK_T('c'): create = true; break; case _TSK_T('a'): addHash = true; break; case _TSK_T('q'): flags |= TSK_HDB_FLAG_QUICK; break; case _TSK_T('V'): tsk_version_print(stdout); exit(0); default: usage(); } } if ((addHash) && ((idx_type != NULL) || (create))) { tsk_fprintf(stderr, "-a cannot be specified with -c or -i\n"); usage(); } if (OPTIND + 1 > argc) { tsk_fprintf(stderr, "Error: You must provide the source hash database location\n"); usage(); } db_file = argv[OPTIND++]; // Running in create mode (-c option). Make a new hash database and exit. if (create) { if (idx_type != NULL) { tsk_fprintf(stderr, "-c and -i cannot be specified at same time\n"); usage(); } TSK_TCHAR *ext = TSTRRCHR(db_file, _TSK_T('.')); if ((NULL != ext) && (TSTRLEN(ext) >= 4) && (TSTRCMP(ext, _TSK_T(".kdb")) == 0)) { if (0 == tsk_hdb_create(db_file)) { tsk_fprintf(stdout, "New database %" PRIttocTSK" created\n", db_file); return 0; } else { tsk_fprintf(stderr, "Failed to create new database %" PRIttocTSK"\n", db_file); return 1; } } else { tsk_fprintf(stderr, "New database path must end in .kdb extension\n"); return 1; } } // Opening an existing database. if ((hdb_info = tsk_hdb_open(db_file, TSK_HDB_OPEN_NONE)) == NULL) { tsk_error_print(stderr); return 1; } // Now that the database is open and its type is known, if running in add hashes mode (-a option) // see if it takes updates. if (addHash && !tsk_hdb_accepts_updates(hdb_info)) { tsk_fprintf(stderr, "-a option specified, but the specified database does not allow hashes to be added\n"); usage(); } // Running in indexing mode (-i option). Create an index file and exit. if (idx_type != NULL) { if (lookup_file != NULL) { tsk_fprintf(stderr, "'-f' flag can't be used with '-i'\n"); usage(); } if (flags & TSK_HDB_FLAG_QUICK) { tsk_fprintf(stderr, "'-q' flag can't be used with '-i'\n"); usage(); } if (flags & TSK_HDB_FLAG_EXT) { tsk_fprintf(stderr, "'-e' flag can't be used with '-i'\n"); usage(); } if (!tsk_hdb_uses_external_indexes(hdb_info)) { tsk_fprintf(stderr, "Database does not use external indexes, can't be used with '-i'\n"); } if (tsk_hdb_is_idx_only(hdb_info)) { tsk_fprintf(stderr, "Database is index only, can be used for look ups, but can't be used with '-i'\n"); } if (tsk_hdb_make_index(hdb_info, idx_type)) { tsk_error_print(stderr); tsk_hdb_close(hdb_info); return 1; } tsk_fprintf(stdout, "Index created\n"); tsk_hdb_close(hdb_info); return 0; } /* Either lookup hash values or add them to DB. * Check if the values were passed on the command line or via a file */ if (OPTIND < argc) { if ((OPTIND + 1 < argc) && (flags & TSK_HDB_FLAG_QUICK)) { fprintf(stderr, "Error: Only one hash can be given with quick option\n"); usage(); } if ((flags & TSK_HDB_FLAG_EXT) && (flags & TSK_HDB_FLAG_QUICK)) { fprintf(stderr, "'-e' flag can't be used with '-q'\n"); usage(); } if (lookup_file != NULL) { fprintf(stderr, "Error: -f can't be used when hashes are also given\n"); usage(); } /* Loop through all provided hash values */ while (OPTIND < argc) { char htmp[128]; int i; int retval; // convert to char -- lazy way to deal with WCHARs.. for (i = 0; i < 127 && argv[OPTIND][i] != '\0'; i++) { htmp[i] = (char) argv[OPTIND][i]; } htmp[i] = '\0'; if (addHash) { // Write a new hash to the database/index, if it's updateable //@todo support sha1 and sha2-256 retval = tsk_hdb_add_entry(hdb_info, NULL, (const char *)htmp, NULL, NULL, NULL); if (retval == 1) { printf("There was an error adding the hash.\n"); tsk_error_print(stderr); return 1; } else if (retval == 0) { printf("Hash %s added.\n", htmp); } } else { /* Perform lookup */ retval = tsk_hdb_lookup_str(hdb_info, (const char *)htmp, (TSK_HDB_FLAG_ENUM)flags, lookup_act, NULL); if (retval == -1) { tsk_error_print(stderr); return 1; } if (flags & TSK_HDB_FLAG_QUICK) { printf("%d\n", retval); } else if (retval == 0) { print_notfound(htmp); } } OPTIND++; } } /* Hash were given from stdin or a file */ else { char buf[100]; /* If the file was specified, use that - otherwise stdin */ #ifdef TSK_WIN32 HANDLE handle = NULL; if (lookup_file != NULL) { if ((handle = CreateFile(lookup_file, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) { TFPRINTF(stderr, _TSK_T("Error opening hash file: %s\n"), lookup_file); exit(1); } } else { handle = GetStdHandle(STD_INPUT_HANDLE); } #else FILE *handle = NULL; if (lookup_file != NULL) { handle = fopen(lookup_file, "r"); if (!handle) { fprintf(stderr, "Error opening hash file: %s\n", lookup_file); exit(1); } } else { handle = stdin; } #endif while (1) { int retval; memset(buf, 0, 100); #ifdef TSK_WIN32 int done = 0; // win32 doesn't have a fgets equivalent, so we make an equivalent one for (int i = 0; i < 100; i++) { DWORD nread; if (FALSE == ReadFile(handle, &buf[i], (DWORD) 1, &nread, NULL)) { done = 1; break; } // skip the windows CR else if (buf[i] == '\r') { buf[i] = '\0'; i--; continue; } else if (buf[i] == '\n') { break; } } if (done) break; #else if (NULL == fgets(buf, 100, handle)) { break; } #endif /* Remove the newline */ buf[strlen(buf) - 1] = '\0'; retval = tsk_hdb_lookup_str(hdb_info, (const char *)buf, (TSK_HDB_FLAG_ENUM)flags, lookup_act, NULL); if (retval == -1) { tsk_error_print(stderr); return 1; } if (flags & TSK_HDB_FLAG_QUICK) { printf("%d\n", retval); break; } else if (retval == 0) { print_notfound(buf); } } #ifdef TSK_WIN32 if (lookup_file != NULL) CloseHandle(handle); #else if (lookup_file != NULL) fclose(handle); #endif } tsk_hdb_close(hdb_info); return 0; }
int main(int argc, char **argv) { int ch; int fd; struct stat st; int junk; ARGV *ext_argv = 0; /* * Fingerprint executables and core dumps. */ MAIL_VERSION_STAMP_ALLOCATE; /* * Be consistent with file permissions. */ umask(022); /* * To minimize confusion, make sure that the standard file descriptors * are open before opening anything else. XXX Work around for 44BSD where * fstat can return EBADF on an open file descriptor. */ for (fd = 0; fd < 3; fd++) if (fstat(fd, &st) == -1 && (close(fd), open("/dev/null", O_RDWR, 0)) != fd) msg_fatal("open /dev/null: %m"); /* * Set up logging. */ msg_vstream_init(argv[0], VSTREAM_ERR); /* * Parse JCL. */ while ((ch = GETOPT(argc, argv, "aAbc:deE#hmlntv")) > 0) { switch (ch) { case 'a': cmd_mode |= SHOW_SASL_SERV; break; case 'A': cmd_mode |= SHOW_SASL_CLNT; break; case 'b': if (ext_argv) msg_fatal("specify one of -b and -t"); ext_argv = argv_alloc(2); argv_add(ext_argv, "bounce", "-SVnexpand_templates", (char *) 0); break; case 'c': if (setenv(CONF_ENV_PATH, optarg, 1) < 0) msg_fatal("out of memory"); break; case 'd': cmd_mode |= SHOW_DEFS; break; case 'e': cmd_mode |= EDIT_MAIN; break; /* * People, this does not work unless you properly handle default * settings. For example, fast_flush_domains = $relay_domains * must not evaluate to the empty string when relay_domains is * left at its default setting of $mydestination. */ #if 0 case 'E': cmd_mode |= SHOW_EVAL; break; #endif case '#': cmd_mode = COMMENT_OUT; break; case 'h': cmd_mode &= ~SHOW_NAME; break; case 'l': cmd_mode |= SHOW_LOCKS; break; case 'm': cmd_mode |= SHOW_MAPS; break; case 'n': cmd_mode |= SHOW_NONDEF; break; case 't': if (ext_argv) msg_fatal("specify one of -b and -t"); ext_argv = argv_alloc(2); argv_add(ext_argv, "bounce", "-SVndump_templates", (char *) 0); break; case 'v': msg_verbose++; break; default: msg_fatal("usage: %s [-a (server SASL types)] [-A (client SASL types)] [-b (bounce templates)] [-c config_dir] [-d (defaults)] [-e (edit)] [-# (comment-out)] [-h (no names)] [-l (lock types)] [-m (map types)] [-n (non-defaults)] [-v] [name...]", argv[0]); } } /* * Sanity check. */ junk = (cmd_mode & (SHOW_DEFS | SHOW_NONDEF | SHOW_MAPS | SHOW_LOCKS | EDIT_MAIN | SHOW_SASL_SERV | SHOW_SASL_CLNT | COMMENT_OUT)); if (junk != 0 && ((junk != SHOW_DEFS && junk != SHOW_NONDEF && junk != SHOW_MAPS && junk != SHOW_LOCKS && junk != EDIT_MAIN && junk != SHOW_SASL_SERV && junk != SHOW_SASL_CLNT && junk != COMMENT_OUT) || ext_argv != 0)) msg_fatal("specify one of -a, -A, -b, -d, -e, -#, -m, -l and -n"); /* * Display bounce template information and exit. */ if (ext_argv) { if (argv[optind]) { if (argv[optind + 1]) msg_fatal("options -b and -t require at most one template file"); argv_add(ext_argv, "-o", concatenate(VAR_BOUNCE_TMPL, "=", argv[optind], (char *) 0), (char *) 0); } /* Grr... */ argv_add(ext_argv, "-o", concatenate(VAR_QUEUE_DIR, "=", ".", (char *) 0), (char *) 0); mail_conf_read(); mail_run_replace(var_daemon_dir, ext_argv->argv); /* NOTREACHED */ } /* * If showing map types, show them and exit */ if (cmd_mode & SHOW_MAPS) { mail_dict_init(); show_maps(); } /* * If showing locking methods, show them and exit */ else if (cmd_mode & SHOW_LOCKS) { show_locks(); } /* * If showing SASL plug-in types, show them and exit */ else if (cmd_mode & SHOW_SASL_SERV) { show_sasl(SHOW_SASL_SERV); } else if (cmd_mode & SHOW_SASL_CLNT) { show_sasl(SHOW_SASL_CLNT); } /* * Edit main.cf. */ else if (cmd_mode & (EDIT_MAIN | COMMENT_OUT)) { edit_parameters(cmd_mode, argc - optind, argv + optind); } /* * If showing non-default values, read main.cf. */ else { if ((cmd_mode & SHOW_DEFS) == 0) { read_parameters(); set_parameters(); } /* * Throw together all parameters and show the asked values. */ hash_parameters(); show_parameters(cmd_mode, argv + optind); } vstream_fflush(VSTREAM_OUT); exit(0); }
int main(int argc, char **argv) { char *path_name; int ch; int fd; char *slash; struct stat st; int postalias_flags = POSTALIAS_FLAG_AS_OWNER | POSTALIAS_FLAG_SAVE_PERM; int open_flags = O_RDWR | O_CREAT | O_TRUNC; int dict_flags = (DICT_FLAG_DUP_WARN | DICT_FLAG_FOLD_FIX | DICT_FLAG_UTF8_REQUEST); char *query = 0; char *delkey = 0; int sequence = 0; int found; ARGV *import_env; /* * Fingerprint executables and core dumps. */ MAIL_VERSION_STAMP_ALLOCATE; /* * Be consistent with file permissions. */ umask(022); /* * To minimize confusion, make sure that the standard file descriptors * are open before opening anything else. XXX Work around for 44BSD where * fstat can return EBADF on an open file descriptor. */ for (fd = 0; fd < 3; fd++) if (fstat(fd, &st) == -1 && (close(fd), open("/dev/null", O_RDWR, 0)) != fd) msg_fatal("open /dev/null: %m"); /* * Process environment options as early as we can. We are not set-uid, * and we are supposed to be running in a controlled environment. */ if (getenv(CONF_ENV_VERB)) msg_verbose = 1; /* * Initialize. Set up logging, read the global configuration file and * extract configuration information. */ if ((slash = strrchr(argv[0], '/')) != 0 && slash[1]) argv[0] = slash + 1; msg_vstream_init(argv[0], VSTREAM_ERR); msg_syslog_init(mail_task(argv[0]), LOG_PID, LOG_FACILITY); /* * Check the Postfix library version as soon as we enable logging. */ MAIL_VERSION_CHECK; /* * Parse JCL. */ while ((ch = GETOPT(argc, argv, "Nc:d:finopq:rsuvw")) > 0) { switch (ch) { default: usage(argv[0]); break; case 'N': dict_flags |= DICT_FLAG_TRY1NULL; dict_flags &= ~DICT_FLAG_TRY0NULL; break; case 'c': if (setenv(CONF_ENV_PATH, optarg, 1) < 0) msg_fatal("out of memory"); break; case 'd': if (sequence || query || delkey) msg_fatal("specify only one of -s -q or -d"); delkey = optarg; break; case 'f': dict_flags &= ~DICT_FLAG_FOLD_FIX; break; case 'i': open_flags &= ~O_TRUNC; break; case 'n': dict_flags |= DICT_FLAG_TRY0NULL; dict_flags &= ~DICT_FLAG_TRY1NULL; break; case 'o': postalias_flags &= ~POSTALIAS_FLAG_AS_OWNER; break; case 'p': postalias_flags &= ~POSTALIAS_FLAG_SAVE_PERM; break; case 'q': if (sequence || query || delkey) msg_fatal("specify only one of -s -q or -d"); query = optarg; break; case 'r': dict_flags &= ~(DICT_FLAG_DUP_WARN | DICT_FLAG_DUP_IGNORE); dict_flags |= DICT_FLAG_DUP_REPLACE; break; case 's': if (query || delkey) msg_fatal("specify only one of -s or -q or -d"); sequence = 1; break; case 'u': dict_flags &= ~DICT_FLAG_UTF8_REQUEST; break; case 'v': msg_verbose++; break; case 'w': dict_flags &= ~(DICT_FLAG_DUP_WARN | DICT_FLAG_DUP_REPLACE); dict_flags |= DICT_FLAG_DUP_IGNORE; break; } } mail_conf_read(); /* Enforce consistent operation of different Postfix parts. */ import_env = mail_parm_split(VAR_IMPORT_ENVIRON, var_import_environ); update_env(import_env->argv); argv_free(import_env); /* Re-evaluate mail_task() after reading main.cf. */ msg_syslog_init(mail_task(argv[0]), LOG_PID, LOG_FACILITY); mail_dict_init(); /* * Use the map type specified by the user, or fall back to a default * database type. */ if (delkey) { /* remove entry */ if (optind + 1 > argc) usage(argv[0]); if (strcmp(delkey, "-") == 0) exit(postalias_deletes(VSTREAM_IN, argv + optind, argc - optind, dict_flags | DICT_FLAG_LOCK) == 0); found = 0; while (optind < argc) { if ((path_name = split_at(argv[optind], ':')) != 0) { found |= postalias_delete(argv[optind], path_name, delkey, dict_flags | DICT_FLAG_LOCK); } else { found |= postalias_delete(var_db_type, argv[optind], delkey, dict_flags | DICT_FLAG_LOCK); } optind++; } exit(found ? 0 : 1); } else if (query) { /* query map(s) */ if (optind + 1 > argc) usage(argv[0]); if (strcmp(query, "-") == 0) exit(postalias_queries(VSTREAM_IN, argv + optind, argc - optind, dict_flags | DICT_FLAG_LOCK) == 0); while (optind < argc) { if ((path_name = split_at(argv[optind], ':')) != 0) { found = postalias_query(argv[optind], path_name, query, dict_flags | DICT_FLAG_LOCK); } else { found = postalias_query(var_db_type, argv[optind], query, dict_flags | DICT_FLAG_LOCK); } if (found) exit(0); optind++; } exit(1); } else if (sequence) { while (optind < argc) { if ((path_name = split_at(argv[optind], ':')) != 0) { postalias_seq(argv[optind], path_name, dict_flags | DICT_FLAG_LOCK); } else { postalias_seq(var_db_type, argv[optind], dict_flags | DICT_FLAG_LOCK); } exit(0); } exit(1); } else { /* create/update map(s) */ if (optind + 1 > argc) usage(argv[0]); while (optind < argc) { if ((path_name = split_at(argv[optind], ':')) != 0) { postalias(argv[optind], path_name, postalias_flags, open_flags, dict_flags); } else { postalias(var_db_type, argv[optind], postalias_flags, open_flags, dict_flags); } optind++; } exit(0); } }
int main(int argc, char **argv1) { TSK_IMG_TYPE_ENUM imgtype = TSK_IMG_TYPE_DETECT; TSK_IMG_INFO *img; TSK_OFF_T imgaddr = 0; TSK_FS_TYPE_ENUM fstype = TSK_FS_TYPE_DETECT; TSK_FS_INFO *fs; TSK_INUM_T inum; int ch; TSK_TCHAR *cp; int32_t sec_skew = 0; /* When > 0 this is the number of blocks to print, used for -B arg */ TSK_DADDR_T numblock = 0; TSK_TCHAR **argv; unsigned int ssize = 0; #ifdef TSK_WIN32 // On Windows, get the wide arguments (mingw doesn't support wmain) argv = CommandLineToArgvW(GetCommandLineW(), &argc); if (argv == NULL) { fprintf(stderr, "Error getting wide arguments\n"); exit(1); } #else argv = (TSK_TCHAR **) argv1; #endif progname = argv[0]; setlocale(LC_ALL, ""); while ((ch = GETOPT(argc, argv, _TSK_T("b:B:f:i:o:s:vVz:"))) > 0) { switch (ch) { case _TSK_T('?'): default: TFPRINTF(stderr, _TSK_T("Invalid argument: %s\n"), argv[OPTIND]); usage(); case _TSK_T('B'): numblock = TSTRTOULL(OPTARG, &cp, 0); if (*cp || *cp == *OPTARG || numblock < 1) { TFPRINTF(stderr, _TSK_T ("invalid argument: block count must be positive: %s\n"), OPTARG); usage(); } break; case _TSK_T('b'): ssize = (unsigned int) TSTRTOUL(OPTARG, &cp, 0); if (*cp || *cp == *OPTARG || ssize < 1) { TFPRINTF(stderr, _TSK_T ("invalid argument: sector size must be positive: %s\n"), OPTARG); usage(); } break; case _TSK_T('f'): if (TSTRCMP(OPTARG, _TSK_T("list")) == 0) { tsk_fs_type_print(stderr); exit(1); } fstype = tsk_fs_type_toid(OPTARG); if (fstype == TSK_FS_TYPE_UNSUPP) { TFPRINTF(stderr, _TSK_T("Unsupported file system type: %s\n"), OPTARG); usage(); } break; case _TSK_T('i'): if (TSTRCMP(OPTARG, _TSK_T("list")) == 0) { tsk_img_type_print(stderr); exit(1); } imgtype = tsk_img_type_toid(OPTARG); if (imgtype == TSK_IMG_TYPE_UNSUPP) { TFPRINTF(stderr, _TSK_T("Unsupported image type: %s\n"), OPTARG); usage(); } break; case _TSK_T('o'): if ((imgaddr = tsk_parse_offset(OPTARG)) == -1) { tsk_error_print(stderr); exit(1); } break; case _TSK_T('s'): sec_skew = TATOI(OPTARG); break; case _TSK_T('v'): tsk_verbose++; break; case _TSK_T('V'): tsk_version_print(stdout); exit(0); case _TSK_T('z'): { TSK_TCHAR envstr[32]; TSNPRINTF(envstr, 32, _TSK_T("TZ=%s"), OPTARG); if (0 != TPUTENV(envstr)) { tsk_fprintf(stderr, "error setting environment"); exit(1); } TZSET(); } break; } } /* We need at least two more argument */ if (OPTIND + 1 >= argc) { tsk_fprintf(stderr, "Missing image name and/or address\n"); usage(); } /* if we are given the inode in the inode-type-id form, then ignore * the other stuff w/out giving an error * * This will make scripting easier */ if (tsk_fs_parse_inum(argv[argc - 1], &inum, NULL, NULL, NULL, NULL)) { TFPRINTF(stderr, _TSK_T("Invalid inode number: %s"), argv[argc - 1]); usage(); } /* * Open the file system. */ if ((img = tsk_img_open(argc - OPTIND - 1, &argv[OPTIND], imgtype, ssize)) == NULL) { tsk_error_print(stderr); exit(1); } if ((imgaddr * img->sector_size) >= img->size) { tsk_fprintf(stderr, "Sector offset supplied is larger than disk image (maximum: %" PRIu64 ")\n", img->size / img->sector_size); exit(1); } if ((fs = tsk_fs_open_img(img, imgaddr * img->sector_size, fstype)) == NULL) { tsk_error_print(stderr); if (tsk_error_get_errno() == TSK_ERR_FS_UNSUPTYPE) tsk_fs_type_print(stderr); img->close(img); exit(1); } if (inum > fs->last_inum) { tsk_fprintf(stderr, "Metadata address is too large for image (%" PRIuINUM ")\n", fs->last_inum); fs->close(fs); img->close(img); exit(1); } if (inum < fs->first_inum) { tsk_fprintf(stderr, "Metadata address is too small for image (%" PRIuINUM ")\n", fs->first_inum); fs->close(fs); img->close(img); exit(1); } if (fs->istat(fs, stdout, inum, numblock, sec_skew)) { tsk_error_print(stderr); fs->close(fs); img->close(img); exit(1); } fs->close(fs); img->close(img); exit(0); }
int main (int argc, char *argv[]) { char *dir = NULL; char *spec, *host; char *nspec = NULL; int c, i; int nopt = 0; int vopt = 0; int fopt = 0; int aopt = 0; int dopt = 0; int rfd = -1, wfd = -1; Opt o; diod_log_init (argv[0]); o = opt_create (); opterr = 0; while ((c = GETOPT (argc, argv, OPTIONS, longopts)) != -1) { switch (c) { case 'f': /* --fake-mount */ fopt = 1; break; case 'n': /* --no-mtab */ nopt = 1; break; case 'v': /* --verbose */ vopt++; break; case 'o': /* --options OPT[,OPT]... */ opt_addf (o, "%s", optarg); break; case 'a': /* --9nbd-attach */ aopt++; break; case 'd': /* --9nbd-detach */ dopt++; break; default: usage (); } } /* Take care of 9nbd operations and exit. */ if (aopt) { _nbd_attach (o, argc - optind, argv + optind, nopt, vopt); exit (0); } if (dopt) { _nbd_detach (o, argc - optind, argv + optind, nopt, vopt); exit (0); } if (optind != argc - 2) usage (); if (geteuid () != 0) msg_exit ("you must be root"); spec = argv[optind++]; dir = argv[optind++]; host = _parse_spec (spec, o); _verify_mountpoint (dir); /* Remount - only pass mount flags into the VFS for an existing mount. * Take care of it here and exit. */ if (opt_find (o, "remount")) { if (opt_check_allowed_csv (o, "ro,rw,aname,remount")) msg_exit ("-oremount can only be used with ro,rw"); _diod_remount (o, spec, dir, vopt, fopt); goto done; } /* Ensure uname and access are set, and to diod-compatible values. * The uname user becomes the euid which will be used by munge auth. */ _parse_uname_access (o); if (seteuid (_uname2uid (opt_find (o, "uname"))) < 0) err_exit ("seteuid"); /* We require -otrans=fd because auth occurs in user space, then live fd * is passed to the kernel via -orfdno,wfdno. */ if (!opt_find (o, "trans")) opt_addf (o, "trans=%s", "fd"); else if (!opt_find (o, "trans=fd")) msg_exit ("only -otrans=fd transport is supported"); /* Set msize if not already set. Validate it later. */ if (!opt_find (o, "msize")) opt_addf (o, "msize=%d", DIOD_DEFAULT_MSIZE); /* Only .L version is supported. */ if (!opt_find (o, "version")) opt_addf (o, "version=%s", "9p2000.L"); else if (!opt_find (o, "version=9p2000.L")) msg_exit ("only -oversion=9p2000.L is supported (little p, big L)"); /* Set debug level. */ if (!opt_find (o, "debug")) opt_addf (o, "debug=%d", 0x1); /* send errors to dmesg */ /* Set rwdepth (number of concurrent reads with buffer > msize). * N.B. this option is not upstream yet but unknown options are ignored. */ if (!opt_find (o, "rwdepth")) opt_addf (o, "rwdepth=%d", 1); /* Server is on an inherited file descriptor. * For testing, we start server on a socketpair duped to fd 0. */ if (opt_find (o, "rfdno") || opt_find (o, "wfdno")) { if (!opt_scanf (o, "rfdno=%d", &rfd) || !opt_scanf (o, "wfdno=%d",&wfd)) msg_exit ("-orfdno,wfdno must be used together"); nopt = 1; /* force no mtab */ /* Connect to server on UNIX domain socket */ } else if (host[0] == '/') { if (opt_find (o, "port")) msg_exit ("-oport won't work with UNIX domain socket"); if ((rfd = diod_sock_connect_unix (host, 0)) < 0) exit (1); wfd = rfd; opt_addf (o, "rfdno=%d", rfd); opt_addf (o, "wfdno=%d", wfd); /* Connect to server on IANA port (or user-specified) and host. */ } else { char *port = opt_find (o, "port"); hostlist_iterator_t hi; hostlist_t hl; char *h; if (!port) port = "564"; if (!(hl = hostlist_create (host))) msg_exit ("error parsing host string: %s", host); if (!(hi = hostlist_iterator_create (hl))) msg_exit ("out of memory"); while ((h = hostlist_next (hi))) { if (vopt) msg ("trying to connect to %s:%s", h, port); if ((rfd = diod_sock_connect_inet (h, port, DIOD_SOCK_QUIET)) >= 0) break; } if (h) { /* create new 'spec' string identifying successful host */ char *p = strchr (spec , ':'); int len = strlen (h) + (p ? strlen (p) : 0) + 1; if (!(nspec = malloc (len))) msg_exit ("out of memory"); snprintf (nspec, len, "%s%s", h, p ? p : ""); } hostlist_destroy (hl); if (rfd < 0) msg_exit ("could not connect to server(s), giving up"); wfd = rfd; opt_delete (o, "port"); opt_addf (o, "rfdno=%d", rfd); opt_addf (o, "wfdno=%d", wfd); } NP_ASSERT (opt_find (o, "trans=fd")); NP_ASSERT (opt_scanf (o, "msize=%d", &i)); NP_ASSERT (opt_find (o, "version=9p2000.L")); NP_ASSERT (opt_scanf (o, "debug=%d", &i) || opt_scanf (o, "debug=%x", &i)); NP_ASSERT (opt_scanf (o, "wfdno=%d", &i) && opt_scanf (o, "rfdno=%d", &i)); NP_ASSERT ((opt_find (o, "access=user") && opt_find(o, "uname=root")) || (opt_scanf (o, "access=%d", &i) && opt_find(o, "uname"))); NP_ASSERT (!opt_find (o, "port")); _diod_mount (o, rfd, wfd, nspec ? nspec : spec, dir, vopt, fopt, nopt); done: opt_destroy (o); exit (0); }
/* main - open file system, list inode info */ int main(int argc, char **argv1) { TSK_IMG_TYPE_ENUM imgtype = TSK_IMG_TYPE_DETECT; TSK_IMG_INFO *img; TSK_OFF_T imgaddr = 0; TSK_FS_TYPE_ENUM fstype = TSK_FS_TYPE_DETECT; TSK_FS_INFO *fs; TSK_TCHAR *cp, *dash; TSK_INUM_T istart = 0, ilast = 0; int ch; int flags = TSK_FS_META_FLAG_UNALLOC | TSK_FS_META_FLAG_USED; int ils_flags = 0; int set_range = 1; TSK_TCHAR *image = NULL; int32_t sec_skew = 0; TSK_TCHAR **argv; unsigned int ssize = 0; #ifdef TSK_WIN32 // On Windows, get the wide arguments (mingw doesn't support wmain) argv = CommandLineToArgvW(GetCommandLineW(), &argc); if (argv == NULL) { fprintf(stderr, "Error getting wide arguments\n"); exit(1); } #else argv = (TSK_TCHAR **) argv1; #endif progname = argv[0]; setlocale(LC_ALL, ""); /* * Provide convenience options for the most commonly selected feature * combinations. */ while ((ch = GETOPT(argc, argv, _TSK_T("aAb:ef:i:lLmo:Oprs:vVzZ"))) > 0) { switch (ch) { case _TSK_T('?'): default: TFPRINTF(stderr, _TSK_T("Invalid argument: %s\n"), argv[OPTIND]); usage(); case _TSK_T('b'): ssize = (unsigned int) TSTRTOUL(OPTARG, &cp, 0); if (*cp || *cp == *OPTARG || ssize < 1) { TFPRINTF(stderr, _TSK_T ("invalid argument: sector size must be positive: %s\n"), OPTARG); usage(); } break; case _TSK_T('f'): if (TSTRCMP(OPTARG, _TSK_T("list")) == 0) { tsk_fs_type_print(stderr); exit(1); } fstype = tsk_fs_type_toid(OPTARG); if (fstype == TSK_FS_TYPE_UNSUPP) { TFPRINTF(stderr, _TSK_T("Unsupported file system type: %s\n"), OPTARG); usage(); } break; case _TSK_T('i'): if (TSTRCMP(OPTARG, _TSK_T("list")) == 0) { tsk_img_type_print(stderr); exit(1); } imgtype = tsk_img_type_toid(OPTARG); if (imgtype == TSK_IMG_TYPE_UNSUPP) { TFPRINTF(stderr, _TSK_T("Unsupported image type: %s\n"), OPTARG); usage(); } break; case _TSK_T('e'): flags |= (TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_UNALLOC); flags &= ~TSK_FS_META_FLAG_USED; break; case _TSK_T('m'): ils_flags |= TSK_FS_ILS_MAC; break; case _TSK_T('o'): if ((imgaddr = tsk_parse_offset(OPTARG)) == -1) { tsk_error_print(stderr); exit(1); } break; case _TSK_T('O'): flags |= TSK_FS_META_FLAG_UNALLOC; flags &= ~TSK_FS_META_FLAG_ALLOC; ils_flags |= TSK_FS_ILS_OPEN; break; case _TSK_T('p'): flags |= (TSK_FS_META_FLAG_ORPHAN | TSK_FS_META_FLAG_UNALLOC); flags &= ~TSK_FS_META_FLAG_ALLOC; break; case _TSK_T('r'): flags |= (TSK_FS_META_FLAG_UNALLOC | TSK_FS_META_FLAG_USED); flags &= ~TSK_FS_META_FLAG_ALLOC; break; case _TSK_T('s'): sec_skew = TATOI(OPTARG); break; case _TSK_T('v'): tsk_verbose++; break; case _TSK_T('V'): tsk_version_print(stdout); exit(0); /* * Provide fine controls to tweak one feature at a time. */ case _TSK_T('a'): flags |= TSK_FS_META_FLAG_ALLOC; break; case _TSK_T('A'): flags |= TSK_FS_META_FLAG_UNALLOC; break; case _TSK_T('l'): ils_flags |= TSK_FS_ILS_LINK; break; case _TSK_T('L'): ils_flags |= TSK_FS_ILS_UNLINK; break; case _TSK_T('z'): flags |= TSK_FS_META_FLAG_UNUSED; break; case _TSK_T('Z'): flags |= TSK_FS_META_FLAG_USED; break; } } if (OPTIND >= argc) { tsk_fprintf(stderr, "Missing image name\n"); usage(); } if ((ils_flags & TSK_FS_ILS_LINK) && (ils_flags & TSK_FS_ILS_UNLINK)) { tsk_fprintf(stderr, "ERROR: Only linked or unlinked should be used\n"); usage(); } /* We need to determine if an inode or inode range was given */ if ((dash = TSTRCHR(argv[argc - 1], _TSK_T('-'))) == NULL) { /* Check if is a single number */ istart = TSTRTOULL(argv[argc - 1], &cp, 0); if (*cp || *cp == *argv[argc - 1]) { /* Not a number - consider it a file name */ image = argv[OPTIND]; if ((img = tsk_img_open(argc - OPTIND, &argv[OPTIND], imgtype, ssize)) == NULL) { tsk_error_print(stderr); exit(1); } if ((imgaddr * img->sector_size) >= img->size) { tsk_fprintf(stderr, "Sector offset supplied is larger than disk image (maximum: %" PRIu64 ")\n", img->size / img->sector_size); exit(1); } } else { /* Single address set end addr to start */ ilast = istart; set_range = 0; image = argv[OPTIND]; if ((img = tsk_img_open(argc - OPTIND - 1, &argv[OPTIND], imgtype, ssize)) == NULL) { tsk_error_print(stderr); exit(1); } if ((imgaddr * img->sector_size) >= img->size) { tsk_fprintf(stderr, "Sector offset supplied is larger than disk image (maximum: %" PRIu64 ")\n", img->size / img->sector_size); exit(1); } } } else { /* We have a dash, but it could be part of the file name */ *dash = '\0'; istart = TSTRTOULL(argv[argc - 1], &cp, 0); if (*cp || *cp == *argv[argc - 1]) { /* Not a number - consider it a file name */ *dash = _TSK_T('-'); image = argv[OPTIND]; if ((img = tsk_img_open(argc - OPTIND, &argv[OPTIND], imgtype, ssize)) == NULL) { tsk_error_print(stderr); exit(1); } if ((imgaddr * img->sector_size) >= img->size) { tsk_fprintf(stderr, "Sector offset supplied is larger than disk image (maximum: %" PRIu64 ")\n", img->size / img->sector_size); exit(1); } } else { dash++; ilast = TSTRTOULL(dash, &cp, 0); if (*cp || *cp == *dash) { /* Not a number - consider it a file name */ dash--; *dash = '-'; image = argv[OPTIND]; if ((img = tsk_img_open(argc - OPTIND, &argv[OPTIND], imgtype, ssize)) == NULL) { tsk_error_print(stderr); exit(1); } if ((imgaddr * img->sector_size) >= img->size) { tsk_fprintf(stderr, "Sector offset supplied is larger than disk image (maximum: %" PRIu64 ")\n", img->size / img->sector_size); exit(1); } } else { set_range = 0; /* It was a block range, so do not include it in the open */ image = argv[OPTIND]; if ((img = tsk_img_open(argc - OPTIND - 1, &argv[OPTIND], imgtype, ssize)) == NULL) { tsk_error_print(stderr); exit(1); } if ((imgaddr * img->sector_size) >= img->size) { tsk_fprintf(stderr, "Sector offset supplied is larger than disk image (maximum: %" PRIu64 ")\n", img->size / img->sector_size); exit(1); } } } } if ((fs = tsk_fs_open_img(img, imgaddr * img->sector_size, fstype)) == NULL) { tsk_error_print(stderr); if (tsk_errno == TSK_ERR_FS_UNSUPTYPE) tsk_fs_type_print(stderr); img->close(img); exit(1); } /* do we need to set the range or just check them? */ if (set_range) { istart = fs->first_inum; ilast = fs->last_inum; } else { if (istart < fs->first_inum) istart = fs->first_inum; if (ilast > fs->last_inum) ilast = fs->last_inum; } /* NTFS uses alloc and link different than UNIX so change * the default behavior * * The link value can be > 0 on deleted files (even when closed) */ /* NTFS and FAT have no notion of deleted but still open */ if ((ils_flags & TSK_FS_ILS_OPEN) && (TSK_FS_TYPE_ISNTFS(fs->ftype) || TSK_FS_TYPE_ISFAT(fs->ftype))) { fprintf(stderr, "Error: '-O' argument does not work with NTFS and FAT images\n"); exit(1); } if (tsk_fs_ils(fs, (TSK_FS_ILS_FLAG_ENUM) ils_flags, istart, ilast, (TSK_FS_META_FLAG_ENUM) flags, sec_skew, image)) { tsk_error_print(stderr); fs->close(fs); img->close(img); exit(1); } fs->close(fs); img->close(img); exit(0); }
int main(int argc, char **argv1) { TSK_IMG_TYPE_ENUM imgtype = TSK_IMG_TYPE_DETECT; TSK_IMG_INFO *img; TSK_OFF_T imgaddr = 0; TSK_FS_TYPE_ENUM fstype = TSK_FS_TYPE_DETECT; TSK_FS_INFO *fs; TSK_INUM_T inum; int ch; TSK_DADDR_T blk; TSK_TCHAR *cp; TSK_TCHAR **argv; unsigned int ssize = 0; #ifdef TSK_WIN32 // On Windows, get the wide arguments (mingw doesn't support wmain) argv = CommandLineToArgvW(GetCommandLineW(), &argc); if (argv == NULL) { fprintf(stderr, "Error getting wide arguments\n"); exit(1); } #else argv = (TSK_TCHAR **) argv1; #endif progname = argv[0]; setlocale(LC_ALL, ""); while ((ch = GETOPT(argc, argv, _TSK_T("b:f:i:o:vV"))) > 0) { switch (ch) { case _TSK_T('?'): default: TFPRINTF(stderr, _TSK_T("Invalid argument: %s\n"), argv[OPTIND]); usage(); case _TSK_T('b'): ssize = (unsigned int) TSTRTOUL(OPTARG, &cp, 0); if (*cp || *cp == *OPTARG || ssize < 1) { TFPRINTF(stderr, _TSK_T ("invalid argument: sector size must be positive: %s\n"), OPTARG); usage(); } break; case _TSK_T('f'): if (TSTRCMP(OPTARG, _TSK_T("list")) == 0) { tsk_fs_type_print(stderr); exit(1); } fstype = tsk_fs_type_toid(OPTARG); if (fstype == TSK_FS_TYPE_UNSUPP) { TFPRINTF(stderr, _TSK_T("Unsupported file system type: %s\n"), OPTARG); usage(); } break; case _TSK_T('i'): if (TSTRCMP(OPTARG, _TSK_T("list")) == 0) { tsk_img_type_print(stderr); exit(1); } imgtype = tsk_img_type_toid(OPTARG); if (imgtype == TSK_IMG_TYPE_UNSUPP) { TFPRINTF(stderr, _TSK_T("Unsupported image type: %s\n"), OPTARG); usage(); } break; case _TSK_T('o'): if ((imgaddr = tsk_parse_offset(OPTARG)) == -1) { tsk_error_print(stderr); exit(1); } break; case _TSK_T('v'): tsk_verbose++; break; case _TSK_T('V'): tsk_version_print(stdout); exit(0); } } /* We need at least two more arguments */ if (OPTIND + 1 >= argc) { tsk_fprintf(stderr, "Missing image name and/or block address\n"); usage(); } blk = TSTRTOULL(argv[argc - 1], &cp, 0); if (*cp || *cp == *argv[argc - 1]) { TFPRINTF(stderr, _TSK_T("bad block number: %s"), argv[argc - 1]); exit(1); } /* Do we have an inode as well? */ if (tsk_fs_parse_inum(argv[argc - 2], &inum, NULL, NULL, NULL, NULL)) { /* Not a number therefore an image */ if ((img = tsk_img_open(argc - OPTIND - 1, &argv[OPTIND], imgtype, ssize)) == NULL) { tsk_error_print(stderr); exit(1); } if ((imgaddr * img->sector_size) >= img->size) { tsk_fprintf(stderr, "Sector offset supplied is larger than disk image (maximum: %" PRIu64 ")\n", img->size / img->sector_size); exit(1); } if ((fs = tsk_fs_open_img(img, imgaddr * img->sector_size, fstype)) == NULL) { tsk_error_print(stderr); if (tsk_errno == TSK_ERR_FS_UNSUPTYPE) tsk_fs_type_print(stderr); img->close(img); exit(1); } inum = fs->journ_inum; } else { if ((img = tsk_img_open(argc - OPTIND - 2, &argv[OPTIND], imgtype, ssize)) == NULL) { tsk_error_print(stderr); exit(1); } if ((fs = tsk_fs_open_img(img, imgaddr * img->sector_size, fstype)) == NULL) { tsk_error_print(stderr); if (tsk_errno == TSK_ERR_FS_UNSUPTYPE) tsk_fs_type_print(stderr); img->close(img); exit(1); } } if (inum > fs->last_inum) { tsk_fprintf(stderr, "Inode value is too large for image (%" PRIuINUM ")\n", fs->last_inum); fs->close(fs); img->close(img); exit(1); } if (inum < fs->first_inum) { tsk_fprintf(stderr, "Inode value is too small for image (%" PRIuINUM ")\n", fs->first_inum); fs->close(fs); img->close(img); exit(1); } if (fs->jopen == NULL) { tsk_fprintf(stderr, "Journal support does not exist for this file system\n"); fs->close(fs); img->close(img); exit(1); } #ifdef TSK_WIN32 if (-1 == _setmode(_fileno(stdout), _O_BINARY)) { fprintf(stderr, "jcat: error setting stdout to binary: %s", strerror(errno)); fs->close(fs); img->close(img); exit(1); } #endif if (fs->jopen(fs, inum)) { tsk_error_print(stderr); fs->close(fs); img->close(img); exit(1); } if (fs->jblk_walk(fs, blk, blk, 0, 0, NULL)) { tsk_error_print(stderr); fs->close(fs); img->close(img); exit(1); } fs->close(fs); img->close(img); exit(0); }
int main(int argc, char **argv) { TSK_IMG_TYPE_ENUM imgtype = TSK_IMG_TYPE_DETECT; TSK_IMG_INFO *img; TSK_OFF_T imgaddr = 0; TSK_FS_TYPE_ENUM fstype = TSK_FS_TYPE_DETECT; TSK_FS_INFO *fs; int ch; TSK_TCHAR **argv; unsigned int ssize = 0; TSK_TCHAR *cp; #ifdef TSK_WIN32 // On Windows, get the wide arguments (mingw doesn't support wmain) argv = CommandLineToArgvW(GetCommandLineW(), &argc); if (argv == NULL) { fprintf(stderr, "Error getting wide arguments\n"); exit(1); } #else argv = (TSK_TCHAR **) argv1; #endif progname = argv[0]; while ((ch = GETOPT(argc, argv, "b:f:i:o:vV")) > 0) { switch (ch) { case '?': default: fprintf(stderr, "Invalid argument: %s\n", argv[OPTIND]); usage(); case _TSK_T('b'): ssize = (unsigned int) TSTRTOUL(OPTARG, &cp, 0); if (*cp || *cp == *OPTARG || ssize < 1) { TFPRINTF(stderr, _TSK_T ("invalid argument: sector size must be positive: %s\n"), OPTARG); usage(); } break; case 'f': if (TSTRCMP(OPTARG, _TSK_T("list")) == 0) { tsk_fs_type_print(stderr); exit(1); } fstype = tsk_fs_type_toid(OPTARG); if (fstype == TSK_FS_TYPE_UNSUPP) { TFPRINTF(stderr, _TSK_T("Unsupported file system type: %s\n"), OPTARG); usage(); } break; case 'i': if (TSTRCMP(OPTARG, _TSK_T("list")) == 0) { tsk_img_type_print(stderr); exit(1); } imgtype = tsk_img_type_toid(OPTARG); if (imgtype == TSK_IMG_TYPE_UNSUPP) { TFPRINTF(stderr, _TSK_T("Unsupported image type: %s\n"), OPTARG); usage(); } break; case 'o': if ((imgaddr = tsk_parse_offset(OPTARG)) == -1) { tsk_error_print(stderr); exit(1); } break; case 'v': verbose++; break; case 'V': print_version(stdout); exit(0); } } /* We need at least one more argument */ if (OPTIND >= argc) { fprintf(stderr, "Missing image name\n"); usage(); } img = img_open(imgoff, argc - OPTIND, (const char **) &argv[OPTIND], imgtype, ssize); if (img == NULL) { tsk_error_print(stderr); exit(1); } if (fs = fs_open(img, fstype)) { if (tsk_errno == TSK_ERR_FS_UNSUPTYPE) tsk_print_types(stderr); tsk_error_print(stderr); img->close(img); exit(1); } if (fs->fscheck(fs, stdout)) { tsk_error_print(stderr); fs->close(fs); img->close(img); exit(1); } fs->close(fs); img->close(img); exit(0); }
int main(int argc, char **argv) { char *class; char *service; char *request; int fd; struct stat st; char *slash; int c; /* * To minimize confusion, make sure that the standard file descriptors * are open before opening anything else. XXX Work around for 44BSD where * fstat can return EBADF on an open file descriptor. */ for (fd = 0; fd < 3; fd++) if (fstat(fd, &st) == -1 && (close(fd), open("/dev/null", O_RDWR, 0)) != fd) msg_fatal("open /dev/null: %m"); /* * Process environment options as early as we can. */ if (safe_getenv(CONF_ENV_VERB)) msg_verbose = 1; /* * Initialize. Set up logging, read the global configuration file and * extract configuration information. */ if ((slash = strrchr(argv[0], '/')) != 0 && slash[1]) argv[0] = slash + 1; msg_vstream_init(argv[0], VSTREAM_ERR); set_mail_conf_str(VAR_PROCNAME, var_procname = mystrdup(argv[0])); /* * Parse JCL. */ while ((c = GETOPT(argc, argv, "c:v")) > 0) { switch (c) { default: usage(argv[0]); case 'c': if (setenv(CONF_ENV_PATH, optarg, 1) < 0) msg_fatal("out of memory"); break; case 'v': msg_verbose++; break; } } if (argc != optind + 3) usage(argv[0]); class = argv[optind]; service = argv[optind + 1]; request = argv[optind + 2]; /* * Finish initializations. */ mail_conf_read(); if (chdir(var_queue_dir)) msg_fatal("chdir %s: %m", var_queue_dir); /* * Kick the service. */ if (mail_trigger(class, service, request, strlen(request)) < 0) { msg_warn("Cannot contact class %s service %s - perhaps the mail system is down", class, service); exit(1); } else {
int main(int argc, char **argv) { static char *full_name = 0; /* sendmail -F */ struct stat st; char *slash; char *sender = 0; /* sendmail -f */ int c; int fd; int mode; ARGV *ext_argv; int debug_me = 0; int err; int n; int flags = SM_FLAG_DEFAULT; char *site_to_flush = 0; char *id_to_flush = 0; char *encoding = 0; char *qtime = 0; const char *errstr; uid_t uid; const char *rewrite_context = MAIL_ATTR_RWR_LOCAL; int dsn_notify = 0; const char *dsn_envid = 0; int saved_optind; /* * Fingerprint executables and core dumps. */ MAIL_VERSION_STAMP_ALLOCATE; /* * Be consistent with file permissions. */ umask(022); /* * To minimize confusion, make sure that the standard file descriptors * are open before opening anything else. XXX Work around for 44BSD where * fstat can return EBADF on an open file descriptor. */ for (fd = 0; fd < 3; fd++) if (fstat(fd, &st) == -1 && (close(fd), open("/dev/null", O_RDWR, 0)) != fd) msg_fatal_status(EX_OSERR, "open /dev/null: %m"); /* * The CDE desktop calendar manager leaks a parent file descriptor into * the child process. For the sake of sendmail compatibility we have to * close the file descriptor otherwise mail notification will hang. */ for ( /* void */ ; fd < 100; fd++) (void) close(fd); /* * Process environment options as early as we can. We might be called * from a set-uid (set-gid) program, so be careful with importing * environment variables. */ if (safe_getenv(CONF_ENV_VERB)) msg_verbose = 1; if (safe_getenv(CONF_ENV_DEBUG)) debug_me = 1; /* * Initialize. Set up logging, read the global configuration file and * extract configuration information. Set up signal handlers so that we * can clean up incomplete output. */ if ((slash = strrchr(argv[0], '/')) != 0 && slash[1]) argv[0] = slash + 1; msg_vstream_init(argv[0], VSTREAM_ERR); msg_cleanup(tempfail); msg_syslog_init(mail_task("sendmail"), LOG_PID, LOG_FACILITY); set_mail_conf_str(VAR_PROCNAME, var_procname = mystrdup(argv[0])); /* * Some sites mistakenly install Postfix sendmail as set-uid root. Drop * set-uid privileges only when root, otherwise some systems will not * reset the saved set-userid, which would be a security vulnerability. */ if (geteuid() == 0 && getuid() != 0) { msg_warn("the Postfix sendmail command has set-uid root file permissions"); msg_warn("or the command is run from a set-uid root process"); msg_warn("the Postfix sendmail command must be installed without set-uid root file permissions"); set_ugid(getuid(), getgid()); } /* * Further initialization. Load main.cf first, so that command-line * options can override main.cf settings. Pre-scan the argument list so * that we load the right main.cf file. */ #define GETOPT_LIST "A:B:C:F:GIL:N:O:R:UV:X:b:ce:f:h:imno:p:r:q:tvx" saved_optind = optind; while (argv[OPTIND] != 0) { if (strcmp(argv[OPTIND], "-q") == 0) { /* not getopt compatible */ optind++; continue; } if ((c = GETOPT(argc, argv, GETOPT_LIST)) <= 0) break; if (c == 'C') { VSTRING *buf = vstring_alloc(1); if (setenv(CONF_ENV_PATH, strcmp(sane_basename(buf, optarg), MAIN_CONF_FILE) == 0 ? sane_dirname(buf, optarg) : optarg, 1) < 0) msg_fatal_status(EX_UNAVAILABLE, "out of memory"); vstring_free(buf); } } optind = saved_optind; mail_conf_read(); if (strcmp(var_syslog_name, DEF_SYSLOG_NAME) != 0) msg_syslog_init(mail_task("sendmail"), LOG_PID, LOG_FACILITY); get_mail_conf_str_table(str_table); if (chdir(var_queue_dir)) msg_fatal_status(EX_UNAVAILABLE, "chdir %s: %m", var_queue_dir); signal(SIGPIPE, SIG_IGN); /* * Optionally start the debugger on ourself. This must be done after * reading the global configuration file, because that file specifies * what debugger command to execute. */ if (debug_me) debug_process(); /* * The default mode of operation is determined by the process name. It * can, however, be changed via command-line options (for example, * "newaliases -bp" will show the mail queue). */ if (strcmp(argv[0], "mailq") == 0) { mode = SM_MODE_MAILQ; } else if (strcmp(argv[0], "newaliases") == 0) { mode = SM_MODE_NEWALIAS; } else if (strcmp(argv[0], "smtpd") == 0) { mode = SM_MODE_DAEMON; } else { mode = SM_MODE_ENQUEUE; } /* * Parse JCL. Sendmail has been around for a long time, and has acquired * a large number of options in the course of time. Some options such as * -q are not parsable with GETOPT() and get special treatment. */ #define OPTIND (optind > 0 ? optind : 1) while (argv[OPTIND] != 0) { if (strcmp(argv[OPTIND], "-q") == 0) { if (mode == SM_MODE_DAEMON) msg_warn("ignoring -q option in daemon mode"); else mode = SM_MODE_FLUSHQ; optind++; continue; } if (strcmp(argv[OPTIND], "-V") == 0) { msg_warn("option -V is deprecated with Postfix 2.3; " "specify -XV instead"); argv[OPTIND] = "-XV"; } if (strncmp(argv[OPTIND], "-V", 2) == 0 && strlen(argv[OPTIND]) == 4) { msg_warn("option %s is deprecated with Postfix 2.3; " "specify -X%s instead", argv[OPTIND], argv[OPTIND] + 1); argv[OPTIND] = concatenate("-X", argv[OPTIND] + 1, (char *) 0); } if (strcmp(argv[OPTIND], "-XV") == 0) { verp_delims = var_verp_delims; optind++; continue; } if ((c = GETOPT(argc, argv, GETOPT_LIST)) <= 0) break; switch (c) { default: if (msg_verbose) msg_info("-%c option ignored", c); break; case 'n': msg_fatal_status(EX_USAGE, "-%c option not supported", c); case 'B': if (strcmp(optarg, "8BITMIME") == 0)/* RFC 1652 */ encoding = MAIL_ATTR_ENC_8BIT; else if (strcmp(optarg, "7BIT") == 0) /* RFC 1652 */ encoding = MAIL_ATTR_ENC_7BIT; else msg_fatal_status(EX_USAGE, "-B option needs 8BITMIME or 7BIT"); break; case 'F': /* full name */ full_name = optarg; break; case 'G': /* gateway submission */ rewrite_context = MAIL_ATTR_RWR_REMOTE; break; case 'I': /* newaliases */ mode = SM_MODE_NEWALIAS; break; case 'N': if ((dsn_notify = dsn_notify_mask(optarg)) == 0) msg_warn("bad -N option value -- ignored"); break; case 'V': /* DSN, was: VERP */ if (strlen(optarg) > 100) msg_warn("too long -V option value -- ignored"); else if (!allprint(optarg)) msg_warn("bad syntax in -V option value -- ignored"); else dsn_envid = optarg; break; case 'X': switch (*optarg) { default: msg_fatal_status(EX_USAGE, "unsupported: -%c%c", c, *optarg); case 'V': /* VERP */ if (verp_delims_verify(optarg + 1) != 0) msg_fatal_status(EX_USAGE, "-V requires two characters from %s", var_verp_filter); verp_delims = optarg + 1; break; } break; case 'b': switch (*optarg) { default: msg_fatal_status(EX_USAGE, "unsupported: -%c%c", c, *optarg); case 'd': /* daemon mode */ if (mode == SM_MODE_FLUSHQ) msg_warn("ignoring -q option in daemon mode"); mode = SM_MODE_DAEMON; break; case 'h': /* print host status */ case 'H': /* flush host status */ mode = SM_MODE_IGNORE; break; case 'i': /* newaliases */ mode = SM_MODE_NEWALIAS; break; case 'm': /* deliver mail */ mode = SM_MODE_ENQUEUE; break; case 'p': /* mailq */ mode = SM_MODE_MAILQ; break; case 's': /* stand-alone mode */ mode = SM_MODE_USER; break; case 'v': /* expand recipients */ flags |= DEL_REQ_FLAG_USR_VRFY; break; } break; case 'f': sender = optarg; break; case 'i': flags &= ~SM_FLAG_AEOF; break; case 'o': switch (*optarg) { default: if (msg_verbose) msg_info("-%c%c option ignored", c, *optarg); break; case 'A': if (optarg[1] == 0) msg_fatal_status(EX_USAGE, "-oA requires pathname"); myfree(var_alias_db_map); var_alias_db_map = mystrdup(optarg + 1); set_mail_conf_str(VAR_ALIAS_DB_MAP, var_alias_db_map); break; case '7': case '8': break; case 'i': flags &= ~SM_FLAG_AEOF; break; case 'm': break; } break; case 'r': /* obsoleted by -f */ sender = optarg; break; case 'q': if (ISDIGIT(optarg[0])) { qtime = optarg; } else if (optarg[0] == 'R') { site_to_flush = optarg + 1; if (*site_to_flush == 0) msg_fatal_status(EX_USAGE, "specify: -qRsitename"); } else if (optarg[0] == 'I') { id_to_flush = optarg + 1; if (*id_to_flush == 0) msg_fatal_status(EX_USAGE, "specify: -qIqueueid"); } else { msg_fatal_status(EX_USAGE, "-q%c is not implemented", optarg[0]); } break; case 't': flags |= SM_FLAG_XRCPT; break; case 'v': msg_verbose++; break; case '?': msg_fatal_status(EX_USAGE, "usage: %s [options]", argv[0]); } } /* * Look for conflicting options and arguments. */ if ((flags & SM_FLAG_XRCPT) && mode != SM_MODE_ENQUEUE) msg_fatal_status(EX_USAGE, "-t can be used only in delivery mode"); if (site_to_flush && mode != SM_MODE_ENQUEUE) msg_fatal_status(EX_USAGE, "-qR can be used only in delivery mode"); if (id_to_flush && mode != SM_MODE_ENQUEUE) msg_fatal_status(EX_USAGE, "-qI can be used only in delivery mode"); if (flags & DEL_REQ_FLAG_USR_VRFY) { if (flags & SM_FLAG_XRCPT) msg_fatal_status(EX_USAGE, "-t option cannot be used with -bv"); if (dsn_notify) msg_fatal_status(EX_USAGE, "-N option cannot be used with -bv"); if (msg_verbose == 1) msg_fatal_status(EX_USAGE, "-v option cannot be used with -bv"); } /* * The -v option plays double duty. One requests verbose delivery, more * than one requests verbose logging. */ if (msg_verbose == 1 && mode == SM_MODE_ENQUEUE) { msg_verbose = 0; flags |= DEL_REQ_FLAG_RECORD; } /* * Start processing. Everything is delegated to external commands. */ if (qtime && mode != SM_MODE_DAEMON) exit(0); switch (mode) { default: msg_panic("unknown operation mode: %d", mode); /* NOTREACHED */ case SM_MODE_ENQUEUE: if (site_to_flush) { if (argv[OPTIND]) msg_fatal_status(EX_USAGE, "flush site requires no recipient"); ext_argv = argv_alloc(2); argv_add(ext_argv, "postqueue", "-s", site_to_flush, (char *) 0); for (n = 0; n < msg_verbose; n++) argv_add(ext_argv, "-v", (char *) 0); argv_terminate(ext_argv); mail_run_replace(var_command_dir, ext_argv->argv); /* NOTREACHED */ } else if (id_to_flush) { if (argv[OPTIND]) msg_fatal_status(EX_USAGE, "flush queue_id requires no recipient"); ext_argv = argv_alloc(2); argv_add(ext_argv, "postqueue", "-i", id_to_flush, (char *) 0); for (n = 0; n < msg_verbose; n++) argv_add(ext_argv, "-v", (char *) 0); argv_terminate(ext_argv); mail_run_replace(var_command_dir, ext_argv->argv); /* NOTREACHED */ } else { enqueue(flags, encoding, dsn_envid, dsn_notify, rewrite_context, sender, full_name, argv + OPTIND); exit(0); /* NOTREACHED */ } break; case SM_MODE_MAILQ: if (argv[OPTIND]) msg_fatal_status(EX_USAGE, "display queue mode requires no recipient"); ext_argv = argv_alloc(2); argv_add(ext_argv, "postqueue", "-p", (char *) 0); for (n = 0; n < msg_verbose; n++) argv_add(ext_argv, "-v", (char *) 0); argv_terminate(ext_argv); mail_run_replace(var_command_dir, ext_argv->argv); /* NOTREACHED */ case SM_MODE_FLUSHQ: if (argv[OPTIND]) msg_fatal_status(EX_USAGE, "flush queue mode requires no recipient"); ext_argv = argv_alloc(2); argv_add(ext_argv, "postqueue", "-f", (char *) 0); for (n = 0; n < msg_verbose; n++) argv_add(ext_argv, "-v", (char *) 0); argv_terminate(ext_argv); mail_run_replace(var_command_dir, ext_argv->argv); /* NOTREACHED */ case SM_MODE_DAEMON: if (argv[OPTIND]) msg_fatal_status(EX_USAGE, "daemon mode requires no recipient"); ext_argv = argv_alloc(2); argv_add(ext_argv, "postfix", (char *) 0); for (n = 0; n < msg_verbose; n++) argv_add(ext_argv, "-v", (char *) 0); argv_add(ext_argv, "start", (char *) 0); argv_terminate(ext_argv); err = (mail_run_background(var_command_dir, ext_argv->argv) < 0); argv_free(ext_argv); exit(err); break; case SM_MODE_NEWALIAS: if (argv[OPTIND]) msg_fatal_status(EX_USAGE, "alias initialization mode requires no recipient"); if (*var_alias_db_map == 0) return (0); ext_argv = argv_alloc(2); argv_add(ext_argv, "postalias", (char *) 0); for (n = 0; n < msg_verbose; n++) argv_add(ext_argv, "-v", (char *) 0); argv_split_append(ext_argv, var_alias_db_map, ", \t\r\n"); argv_terminate(ext_argv); mail_run_replace(var_command_dir, ext_argv->argv); /* NOTREACHED */ case SM_MODE_USER: if (argv[OPTIND]) msg_fatal_status(EX_USAGE, "stand-alone mode requires no recipient"); /* The actual enforcement happens in the postdrop command. */ if ((errstr = check_user_acl_byuid(var_submit_acl, uid = getuid())) != 0) msg_fatal_status(EX_NOPERM, "User %s(%ld) is not allowed to submit mail", errstr, (long) uid); ext_argv = argv_alloc(2); argv_add(ext_argv, "smtpd", "-S", (char *) 0); for (n = 0; n < msg_verbose; n++) argv_add(ext_argv, "-v", (char *) 0); argv_terminate(ext_argv); mail_run_replace(var_daemon_dir, ext_argv->argv); /* NOTREACHED */ case SM_MODE_IGNORE: exit(0); /* NOTREACHED */ } }
int main(int argc, char **argv) { char *script; struct stat st; char *slash; int fd; int ch; ARGV *import_env; static const CONFIG_STR_TABLE str_table[] = { VAR_SENDMAIL_PATH, DEF_SENDMAIL_PATH, &var_sendmail_path, 1, 0, VAR_MAILQ_PATH, DEF_MAILQ_PATH, &var_mailq_path, 1, 0, VAR_NEWALIAS_PATH, DEF_NEWALIAS_PATH, &var_newalias_path, 1, 0, VAR_MANPAGE_DIR, DEF_MANPAGE_DIR, &var_manpage_dir, 1, 0, VAR_SAMPLE_DIR, DEF_SAMPLE_DIR, &var_sample_dir, 1, 0, VAR_README_DIR, DEF_README_DIR, &var_readme_dir, 1, 0, VAR_HTML_DIR, DEF_HTML_DIR, &var_html_dir, 1, 0, 0, }; int force_single_instance; ARGV *my_argv; /* * Fingerprint executables and core dumps. */ MAIL_VERSION_STAMP_ALLOCATE; /* * Be consistent with file permissions. */ umask(022); /* * To minimize confusion, make sure that the standard file descriptors * are open before opening anything else. XXX Work around for 44BSD where * fstat can return EBADF on an open file descriptor. */ for (fd = 0; fd < 3; fd++) if (fstat(fd, &st) == -1 && (close(fd), open("/dev/null", O_RDWR, 0)) != fd) msg_fatal("open /dev/null: %m"); /* * Set up diagnostics. XXX What if stdin is the system console during * boot time? It seems a bad idea to log startup errors to the console. * This is UNIX, a system that can run without hand holding. */ if ((slash = strrchr(argv[0], '/')) != 0 && slash[1]) argv[0] = slash + 1; if (isatty(STDERR_FILENO)) msg_vstream_init(argv[0], VSTREAM_ERR); msg_syslog_init(argv[0], LOG_PID, LOG_FACILITY); /* * Check the Postfix library version as soon as we enable logging. */ MAIL_VERSION_CHECK; /* * The mail system must be run by the superuser so it can revoke * privileges for selected operations. That's right - it takes privileges * to toss privileges. */ if (getuid() != 0) { msg_error("to submit mail, use the Postfix sendmail command"); msg_fatal("the postfix command is reserved for the superuser"); } if (unsafe() != 0) msg_fatal("the postfix command must not run as a set-uid process"); /* * Parse switches. */ while ((ch = GETOPT(argc, argv, "c:Dv")) > 0) { switch (ch) { default: msg_fatal("usage: %s [-c config_dir] [-Dv] command", argv[0]); case 'c': if (*optarg != '/') msg_fatal("-c requires absolute pathname"); check_setenv(CONF_ENV_PATH, optarg); break; case 'D': check_setenv(CONF_ENV_DEBUG, ""); break; case 'v': msg_verbose++; check_setenv(CONF_ENV_VERB, ""); break; } } force_single_instance = (getenv(CONF_ENV_PATH) != 0); /* * Copy a bunch of configuration parameters into the environment for easy * access by the maintenance shell script. */ mail_conf_read(); get_mail_conf_str_table(str_table); /* * Environment import filter, to enforce consistent behavior whether this * command is started by hand, or at system boot time. This is necessary * because some shell scripts use environment settings to override * main.cf settings. */ import_env = argv_split(var_import_environ, ", \t\r\n"); clean_env(import_env->argv); argv_free(import_env); check_setenv("PATH", ROOT_PATH); /* sys_defs.h */ check_setenv(CONF_ENV_PATH, var_config_dir);/* mail_conf.h */ check_setenv(VAR_COMMAND_DIR, var_command_dir); /* main.cf */ check_setenv(VAR_DAEMON_DIR, var_daemon_dir); /* main.cf */ check_setenv(VAR_DATA_DIR, var_data_dir); /* main.cf */ check_setenv(VAR_QUEUE_DIR, var_queue_dir); /* main.cf */ check_setenv(VAR_CONFIG_DIR, var_config_dir); /* main.cf */ /* * Do we want to keep adding things here as shell scripts evolve? */ check_setenv(VAR_MAIL_OWNER, var_mail_owner); /* main.cf */ check_setenv(VAR_SGID_GROUP, var_sgid_group); /* main.cf */ check_setenv(VAR_SENDMAIL_PATH, var_sendmail_path); /* main.cf */ check_setenv(VAR_MAILQ_PATH, var_mailq_path); /* main.cf */ check_setenv(VAR_NEWALIAS_PATH, var_newalias_path); /* main.cf */ check_setenv(VAR_MANPAGE_DIR, var_manpage_dir); /* main.cf */ check_setenv(VAR_SAMPLE_DIR, var_sample_dir); /* main.cf */ check_setenv(VAR_README_DIR, var_readme_dir); /* main.cf */ check_setenv(VAR_HTML_DIR, var_html_dir); /* main.cf */ /* * Make sure these directories exist. Run the maintenance scripts with as * current directory the mail database. */ if (chdir(var_command_dir)) msg_fatal("chdir(%s): %m", var_command_dir); if (chdir(var_daemon_dir)) msg_fatal("chdir(%s): %m", var_daemon_dir); if (chdir(var_queue_dir)) msg_fatal("chdir(%s): %m", var_queue_dir); /* * Run the management script. */ if (force_single_instance || argv_split(var_multi_conf_dirs, "\t\r\n, ")->argc == 0) { script = concatenate(var_daemon_dir, "/postfix-script", (char *) 0); if (optind < 1) msg_panic("bad optind value"); argv[optind - 1] = script; execvp(script, argv + optind - 1); msg_fatal("%s: %m", script); } /* * Hand off control to a multi-instance manager. */ else { if (*var_multi_wrapper == 0) msg_fatal("multi-instance support is requested, but %s is empty", VAR_MULTI_WRAPPER); my_argv = argv_split(var_multi_wrapper, " \t\r\n"); do { argv_add(my_argv, argv[optind], (char *) 0); } while (argv[optind++] != 0); execvp(my_argv->argv[0], my_argv->argv); msg_fatal("%s: %m", my_argv->argv[0]); } }
NORETURN trigger_server_main(int argc, char **argv, TRIGGER_SERVER_FN service,...) { const char *myname = "trigger_server_main"; char *root_dir = 0; char *user_name = 0; int debug_me = 0; int daemon_mode = 1; char *service_name = basename(argv[0]); VSTREAM *stream = 0; int delay; int c; int socket_count = 1; int fd; va_list ap; MAIL_SERVER_INIT_FN pre_init = 0; MAIL_SERVER_INIT_FN post_init = 0; MAIL_SERVER_LOOP_FN loop = 0; int key; char buf[TRIGGER_BUF_SIZE]; ssize_t len; char *transport = 0; char *lock_path; VSTRING *why; int alone = 0; int zerolimit = 0; WATCHDOG *watchdog; char *oname_val; char *oname; char *oval; const char *err; char *generation; int msg_vstream_needed = 0; int redo_syslog_init = 0; const char *dsn_filter_title; const char **dsn_filter_maps; /* * Process environment options as early as we can. */ if (getenv(CONF_ENV_VERB)) msg_verbose = 1; if (getenv(CONF_ENV_DEBUG)) debug_me = 1; /* * Don't die when a process goes away unexpectedly. */ signal(SIGPIPE, SIG_IGN); /* * Don't die for frivolous reasons. */ #ifdef SIGXFSZ signal(SIGXFSZ, SIG_IGN); #endif /* * May need this every now and then. */ var_procname = mystrdup(basename(argv[0])); set_mail_conf_str(VAR_PROCNAME, var_procname); /* * Initialize logging and exit handler. Do the syslog first, so that its * initialization completes before we enter the optional chroot jail. */ msg_syslog_init(mail_task(var_procname), LOG_PID, LOG_FACILITY); if (msg_verbose) msg_info("daemon started"); /* * Check the Postfix library version as soon as we enable logging. */ MAIL_VERSION_CHECK; /* * Initialize from the configuration file. Allow command-line options to * override compiled-in defaults or configured parameter values. */ mail_conf_suck(); /* * After database open error, continue execution with reduced * functionality. */ dict_allow_surrogate = 1; /* * Pick up policy settings from master process. Shut up error messages to * stderr, because no-one is going to see them. */ opterr = 0; while ((c = GETOPT(argc, argv, "cdDi:lm:n:o:s:St:uvVz")) > 0) { switch (c) { case 'c': root_dir = "setme"; break; case 'd': daemon_mode = 0; break; case 'D': debug_me = 1; break; case 'i': mail_conf_update(VAR_MAX_IDLE, optarg); break; case 'l': alone = 1; break; case 'm': mail_conf_update(VAR_MAX_USE, optarg); break; case 'n': service_name = optarg; break; case 'o': oname_val = mystrdup(optarg); if ((err = split_nameval(oname_val, &oname, &oval)) != 0) msg_fatal("invalid \"-o %s\" option value: %s", optarg, err); mail_conf_update(oname, oval); if (strcmp(oname, VAR_SYSLOG_NAME) == 0) redo_syslog_init = 1; myfree(oname_val); break; case 's': if ((socket_count = atoi(optarg)) <= 0) msg_fatal("invalid socket_count: %s", optarg); break; case 'S': stream = VSTREAM_IN; break; case 't': transport = optarg; break; case 'u': user_name = "setme"; break; case 'v': msg_verbose++; break; case 'V': if (++msg_vstream_needed == 1) msg_vstream_init(mail_task(var_procname), VSTREAM_ERR); break; case 'z': zerolimit = 1; break; default: msg_fatal("invalid option: %c", c); break; } } /* * Initialize generic parameters. */ mail_params_init(); if (redo_syslog_init) msg_syslog_init(mail_task(var_procname), LOG_PID, LOG_FACILITY); /* * Register higher-level dictionaries and initialize the support for * dynamically-loaded dictionarles. */ mail_dict_init(); /* * If not connected to stdin, stdin must not be a terminal. */ if (daemon_mode && stream == 0 && isatty(STDIN_FILENO)) { msg_vstream_init(var_procname, VSTREAM_ERR); msg_fatal("do not run this command by hand-4"); } /* * Application-specific initialization. */ va_start(ap, service); while ((key = va_arg(ap, int)) != 0) { switch (key) { case MAIL_SERVER_INT_TABLE: get_mail_conf_int_table(va_arg(ap, CONFIG_INT_TABLE *)); break; case MAIL_SERVER_LONG_TABLE: get_mail_conf_long_table(va_arg(ap, CONFIG_LONG_TABLE *)); break; case MAIL_SERVER_STR_TABLE: get_mail_conf_str_table(va_arg(ap, CONFIG_STR_TABLE *)); break; case MAIL_SERVER_BOOL_TABLE: get_mail_conf_bool_table(va_arg(ap, CONFIG_BOOL_TABLE *)); break; case MAIL_SERVER_TIME_TABLE: get_mail_conf_time_table(va_arg(ap, CONFIG_TIME_TABLE *)); break; case MAIL_SERVER_RAW_TABLE: get_mail_conf_raw_table(va_arg(ap, CONFIG_RAW_TABLE *)); break; case MAIL_SERVER_NINT_TABLE: get_mail_conf_nint_table(va_arg(ap, CONFIG_NINT_TABLE *)); break; case MAIL_SERVER_NBOOL_TABLE: get_mail_conf_nbool_table(va_arg(ap, CONFIG_NBOOL_TABLE *)); break; case MAIL_SERVER_PRE_INIT: pre_init = va_arg(ap, MAIL_SERVER_INIT_FN); break; case MAIL_SERVER_POST_INIT: post_init = va_arg(ap, MAIL_SERVER_INIT_FN); break; case MAIL_SERVER_LOOP: loop = va_arg(ap, MAIL_SERVER_LOOP_FN); break; case MAIL_SERVER_EXIT: trigger_server_onexit = va_arg(ap, MAIL_SERVER_EXIT_FN); break; case MAIL_SERVER_PRE_ACCEPT: trigger_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN); break; case MAIL_SERVER_IN_FLOW_DELAY: trigger_server_in_flow_delay = 1; break; case MAIL_SERVER_SOLITARY: if (stream == 0 && !alone) msg_fatal("service %s requires a process limit of 1", service_name); break; case MAIL_SERVER_UNLIMITED: if (stream == 0 && !zerolimit) msg_fatal("service %s requires a process limit of 0", service_name); break; case MAIL_SERVER_PRIVILEGED: if (user_name) msg_fatal("service %s requires privileged operation", service_name); break; case MAIL_SERVER_WATCHDOG: trigger_server_watchdog = *va_arg(ap, int *); break; case MAIL_SERVER_BOUNCE_INIT: dsn_filter_title = va_arg(ap, const char *); dsn_filter_maps = va_arg(ap, const char **); bounce_client_init(dsn_filter_title, *dsn_filter_maps); break; default: msg_panic("%s: unknown argument type: %d", myname, key); } } va_end(ap); if (root_dir) root_dir = var_queue_dir; if (user_name) user_name = var_mail_owner; /* * Can options be required? * * XXX Initially this code was implemented with UNIX-domain sockets, but * Solaris <= 2.5 UNIX-domain sockets misbehave hopelessly when the * client disconnects before the server has accepted the connection. * Symptom: the server accept() fails with EPIPE or EPROTO, but the * socket stays readable, so that the program goes into a wasteful loop. * * The initial fix was to use FIFOs, but those turn out to have their own * problems, witness the workarounds in the fifo_listen() routine. * Therefore we support both FIFOs and UNIX-domain sockets, so that the * user can choose whatever works best. * * Well, I give up. Solaris UNIX-domain sockets still don't work properly, * so it will have to limp along with a streams-specific alternative. */ if (stream == 0) { if (transport == 0) msg_fatal("no transport type specified"); if (strcasecmp(transport, MASTER_XPORT_NAME_UNIX) == 0) trigger_server_accept = trigger_server_accept_local; else if (strcasecmp(transport, MASTER_XPORT_NAME_FIFO) == 0) trigger_server_accept = trigger_server_accept_fifo; #ifdef MASTER_XPORT_NAME_PASS else if (strcasecmp(transport, MASTER_XPORT_NAME_PASS) == 0) trigger_server_accept = trigger_server_accept_pass; #endif else msg_fatal("unsupported transport type: %s", transport); } /* * Retrieve process generation from environment. */ if ((generation = getenv(MASTER_GEN_NAME)) != 0) { if (!alldig(generation)) msg_fatal("bad generation: %s", generation); OCTAL_TO_UNSIGNED(trigger_server_generation, generation); if (msg_verbose) msg_info("process generation: %s (%o)", generation, trigger_server_generation); } /* * Optionally start the debugger on ourself. */ if (debug_me) debug_process(); /* * Traditionally, BSD select() can't handle multiple processes selecting * on the same socket, and wakes up every process in select(). See TCP/IP * Illustrated volume 2 page 532. We avoid select() collisions with an * external lock file. */ if (stream == 0 && !alone) { lock_path = concatenate(DEF_PID_DIR, "/", transport, ".", service_name, (char *) 0); why = vstring_alloc(1); if ((trigger_server_lock = safe_open(lock_path, O_CREAT | O_RDWR, 0600, (struct stat *) 0, -1, -1, why)) == 0) msg_fatal("open lock file %s: %s", lock_path, vstring_str(why)); close_on_exec(vstream_fileno(trigger_server_lock), CLOSE_ON_EXEC); myfree(lock_path); vstring_free(why); } /* * Set up call-back info. */ trigger_server_service = service; trigger_server_name = service_name; trigger_server_argv = argv + optind; /* * Run pre-jail initialization. */ if (chdir(var_queue_dir) < 0) msg_fatal("chdir(\"%s\"): %m", var_queue_dir); if (pre_init) pre_init(trigger_server_name, trigger_server_argv); /* * Optionally, restrict the damage that this process can do. */ resolve_local_init(); tzset(); chroot_uid(root_dir, user_name); /* * Run post-jail initialization. */ if (post_init) post_init(trigger_server_name, trigger_server_argv); /* * Are we running as a one-shot server with the client connection on * standard input? */ if (stream != 0) { if ((len = read(vstream_fileno(stream), buf, sizeof(buf))) <= 0) msg_fatal("read: %m"); service(buf, len, trigger_server_name, trigger_server_argv); vstream_fflush(stream); trigger_server_exit(); } /* * Running as a semi-resident server. Service connection requests. * Terminate when we have serviced a sufficient number of clients, when * no-one has been talking to us for a configurable amount of time, or * when the master process terminated abnormally. */ if (var_idle_limit > 0) event_request_timer(trigger_server_timeout, (void *) 0, var_idle_limit); for (fd = MASTER_LISTEN_FD; fd < MASTER_LISTEN_FD + socket_count; fd++) { event_enable_read(fd, trigger_server_accept, CAST_INT_TO_VOID_PTR(fd)); close_on_exec(fd, CLOSE_ON_EXEC); } event_enable_read(MASTER_STATUS_FD, trigger_server_abort, (void *) 0); close_on_exec(MASTER_STATUS_FD, CLOSE_ON_EXEC); close_on_exec(MASTER_FLOW_READ, CLOSE_ON_EXEC); close_on_exec(MASTER_FLOW_WRITE, CLOSE_ON_EXEC); watchdog = watchdog_create(trigger_server_watchdog, (WATCHDOG_FN) 0, (void *) 0); /* * The event loop, at last. */ while (var_use_limit == 0 || use_count < var_use_limit) { if (trigger_server_lock != 0) { watchdog_stop(watchdog); if (myflock(vstream_fileno(trigger_server_lock), INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) msg_fatal("select lock: %m"); } watchdog_start(watchdog); delay = loop ? loop(trigger_server_name, trigger_server_argv) : -1; event_loop(delay); } trigger_server_exit(); }
int main(int argc, char **argv) { int ch; int fd; struct stat st; ARGV *ext_argv = 0; int param_class = PCF_PARAM_MASK_CLASS; static const NAME_MASK param_class_table[] = { "builtin", PCF_PARAM_FLAG_BUILTIN, "service", PCF_PARAM_FLAG_SERVICE, "user", PCF_PARAM_FLAG_USER, "all", PCF_PARAM_MASK_CLASS, 0, }; ARGV *override_params = 0; /* * Fingerprint executables and core dumps. */ MAIL_VERSION_STAMP_ALLOCATE; /* * Be consistent with file permissions. */ umask(022); /* * To minimize confusion, make sure that the standard file descriptors * are open before opening anything else. XXX Work around for 44BSD where * fstat can return EBADF on an open file descriptor. */ for (fd = 0; fd < 3; fd++) if (fstat(fd, &st) == -1 && (close(fd), open("/dev/null", O_RDWR, 0)) != fd) msg_fatal("open /dev/null: %m"); /* * Set up logging. */ msg_vstream_init(argv[0], VSTREAM_ERR); /* * Parse JCL. */ while ((ch = GETOPT(argc, argv, "aAbc:C:deEfFhlmMno:pPtvxX#")) > 0) { switch (ch) { case 'a': pcf_cmd_mode |= PCF_SHOW_SASL_SERV; break; case 'A': pcf_cmd_mode |= PCF_SHOW_SASL_CLNT; break; case 'b': pcf_cmd_mode |= PCF_EXP_DSN_TEMPL; if (ext_argv) msg_fatal("specify one of -b and -t"); ext_argv = argv_alloc(2); argv_add(ext_argv, "bounce", "-SVnexpand_templates", (char *) 0); break; case 'c': if (setenv(CONF_ENV_PATH, optarg, 1) < 0) msg_fatal("out of memory"); break; case 'C': param_class = name_mask_opt("-C option", param_class_table, optarg, NAME_MASK_ANY_CASE | NAME_MASK_FATAL); break; case 'd': pcf_cmd_mode |= PCF_SHOW_DEFS; break; case 'e': pcf_cmd_mode |= PCF_EDIT_CONF; break; case 'f': pcf_cmd_mode |= PCF_FOLD_LINE; break; case 'F': pcf_cmd_mode |= PCF_MASTER_FLD; break; case '#': pcf_cmd_mode |= PCF_COMMENT_OUT; break; case 'h': pcf_cmd_mode |= PCF_HIDE_NAME; break; case 'l': pcf_cmd_mode |= PCF_SHOW_LOCKS; break; case 'm': pcf_cmd_mode |= PCF_SHOW_MAPS; break; case 'M': pcf_cmd_mode |= PCF_MASTER_ENTRY; break; case 'n': pcf_cmd_mode |= PCF_SHOW_NONDEF; break; case 'o': pcf_cmd_mode |= PCF_MAIN_OVER; if (override_params == 0) override_params = argv_alloc(2); argv_add(override_params, optarg, (char *) 0); break; case 'p': pcf_cmd_mode |= PCF_MAIN_PARAM; break; case 'P': pcf_cmd_mode |= PCF_MASTER_PARAM; break; case 't': pcf_cmd_mode |= PCF_DUMP_DSN_TEMPL; if (ext_argv) msg_fatal("specify one of -b and -t"); ext_argv = argv_alloc(2); argv_add(ext_argv, "bounce", "-SVndump_templates", (char *) 0); break; case 'x': pcf_cmd_mode |= PCF_SHOW_EVAL; break; case 'X': /* This is irreversible, therefore require two-finger action. */ pcf_cmd_mode |= PCF_EDIT_EXCL; break; case 'v': msg_verbose++; break; default: usage(argv[0]); } } /* * Make all options explicit, before checking their compatibility. */ #define PCF_MAIN_OR_MASTER \ (PCF_MAIN_PARAM | PCF_MASTER_ENTRY | PCF_MASTER_FLD | PCF_MASTER_PARAM) if ((pcf_cmd_mode & pcf_incompat_options[0]) == 0) pcf_cmd_mode |= PCF_MAIN_PARAM; if ((pcf_cmd_mode & PCF_MAIN_OR_MASTER) && argv[optind] && strchr(argv[optind], '=')) pcf_cmd_mode |= PCF_EDIT_CONF; /* * Sanity check. */ pcf_check_exclusive_options(pcf_cmd_mode); pcf_check_compat_options(pcf_cmd_mode); if ((pcf_cmd_mode & PCF_EDIT_CONF) && argc == optind) msg_fatal("-e requires name=value argument"); /* * Display bounce template information and exit. */ if (ext_argv) { if (argv[optind]) { if (argv[optind + 1]) msg_fatal("options -b and -t require at most one template file"); argv_add(ext_argv, "-o", concatenate(VAR_BOUNCE_TMPL, "=", argv[optind], (char *) 0), (char *) 0); } /* Grr... */ argv_add(ext_argv, "-o", concatenate(VAR_QUEUE_DIR, "=", ".", (char *) 0), (char *) 0); mail_conf_read(); mail_run_replace(var_daemon_dir, ext_argv->argv); /* NOTREACHED */ } /* * If showing map types, show them and exit */ if (pcf_cmd_mode & PCF_SHOW_MAPS) { mail_dict_init(); pcf_show_maps(); } /* * If showing locking methods, show them and exit */ else if (pcf_cmd_mode & PCF_SHOW_LOCKS) { pcf_show_locks(); } /* * If showing master.cf entries, show them and exit */ else if ((pcf_cmd_mode & (PCF_MASTER_ENTRY | PCF_MASTER_FLD | PCF_MASTER_PARAM)) && !(pcf_cmd_mode & (PCF_EDIT_CONF | PCF_EDIT_EXCL | PCF_COMMENT_OUT))) { pcf_read_master(PCF_FAIL_ON_OPEN_ERROR); pcf_read_parameters(); if (override_params) pcf_set_parameters(override_params->argv); pcf_register_builtin_parameters(basename(argv[0]), getpid()); pcf_register_service_parameters(); pcf_register_user_parameters(); if (pcf_cmd_mode & PCF_MASTER_FLD) pcf_show_master_fields(VSTREAM_OUT, pcf_cmd_mode, argc - optind, argv + optind); else if (pcf_cmd_mode & PCF_MASTER_PARAM) pcf_show_master_params(VSTREAM_OUT, pcf_cmd_mode, argc - optind, argv + optind); else pcf_show_master_entries(VSTREAM_OUT, pcf_cmd_mode, argc - optind, argv + optind); } /* * If showing SASL plug-in types, show them and exit */ else if (pcf_cmd_mode & PCF_SHOW_SASL_SERV) { pcf_show_sasl(PCF_SHOW_SASL_SERV); } else if (pcf_cmd_mode & PCF_SHOW_SASL_CLNT) { pcf_show_sasl(PCF_SHOW_SASL_CLNT); } /* * Edit main.cf or master.cf. */ else if (pcf_cmd_mode & (PCF_EDIT_CONF | PCF_COMMENT_OUT | PCF_EDIT_EXCL)) { if (optind == argc) msg_fatal("missing service argument"); if (pcf_cmd_mode & (PCF_MASTER_ENTRY | PCF_MASTER_FLD | PCF_MASTER_PARAM)) { pcf_edit_master(pcf_cmd_mode, argc - optind, argv + optind); } else { pcf_edit_main(pcf_cmd_mode, argc - optind, argv + optind); } } /* * If showing non-default values, read main.cf. */ else { if ((pcf_cmd_mode & PCF_SHOW_DEFS) == 0) { pcf_read_parameters(); if (override_params) pcf_set_parameters(override_params->argv); } pcf_register_builtin_parameters(basename(argv[0]), getpid()); /* * Add service-dependent parameters (service names from master.cf) * and user-defined parameters ($name macros in parameter values in * main.cf and master.cf, but only if those names have a name=value * in main.cf or master.cf). */ pcf_read_master(PCF_WARN_ON_OPEN_ERROR); pcf_register_service_parameters(); if ((pcf_cmd_mode & PCF_SHOW_DEFS) == 0) pcf_register_user_parameters(); /* * Show the requested values. */ pcf_show_parameters(VSTREAM_OUT, pcf_cmd_mode, param_class, argv + optind); /* * Flag unused parameters. This makes no sense with "postconf -d", * because that ignores all the user-specified parameters and * user-specified macro expansions in main.cf. */ if ((pcf_cmd_mode & PCF_SHOW_DEFS) == 0) { pcf_flag_unused_main_parameters(); pcf_flag_unused_master_parameters(); } } vstream_fflush(VSTREAM_OUT); exit(0); }