| /* |
| * Copyright (c) 2000-2002 Silicon Graphics, Inc. |
| * All Rights Reserved. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it would be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <sys/stat.h> |
| #include <sys/param.h> |
| #include <sys/wait.h> |
| #include <sys/prctl.h> |
| #include <sys/resource.h> |
| #include <time.h> |
| #include <limits.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <signal.h> |
| #include <termios.h> |
| #include <getopt.h> |
| #include <stdint.h> |
| #include <sched.h> |
| #include <pthread.h> |
| #include <assert.h> |
| #include <string.h> |
| #include <uuid/uuid.h> |
| #include <locale.h> |
| |
| #include "config.h" |
| |
| #include "exit.h" |
| #include "types.h" |
| #include "stream.h" |
| #include "cldmgr.h" |
| #include "getopt.h" |
| #include "mlog.h" |
| #include "qlock.h" |
| #include "lock.h" |
| #include "dlog.h" |
| #include "global.h" |
| #include "drive.h" |
| #include "media.h" |
| #include "content.h" |
| #include "inventory.h" |
| |
| #ifdef DUMP |
| /* main.c - main for dump |
| */ |
| #endif /* DUMP */ |
| #ifdef RESTORE |
| /* main.c - main for restore |
| */ |
| #endif /* RESTORE */ |
| |
| |
| /* structure definitions used locally ****************************************/ |
| |
| #ifdef RESTORE |
| #define VMSZ_PER 4 /* proportion of available vm to use in tree */ |
| #endif /* RESTORE */ |
| #define DLOG_TIMEOUT 60 /* time out operator dialog */ |
| #define STOP_TIMEOUT 600 /* seconds after stop req. before abort */ |
| #define ABORT_TIMEOUT 10 /* seconds after abort req. before abort */ |
| #define MINSTACKSZ 0x02000000 |
| #define MAXSTACKSZ 0x08000000 |
| |
| |
| /* declarations of externally defined global symbols *************************/ |
| |
| extern void rmt_turnonmsgs(int); |
| |
| /* forward declarations of locally defined global functions ******************/ |
| |
| void usage(void); |
| bool_t preemptchk(int); |
| |
| |
| /* forward declarations of locally defined static functions ******************/ |
| |
| static bool_t loadoptfile(int *argcp, char ***argvp); |
| static char * stripquotes(char *p); |
| static void shiftleftby1(char *p, char *endp); |
| static void sighandler(int); |
| static int childmain(void *); |
| static bool_t sigint_dialog(void); |
| static char *sigintstr(void); |
| #ifdef DUMP |
| static bool_t set_rlimits(void); |
| #endif /* DUMP */ |
| #ifdef RESTORE |
| static bool_t set_rlimits(size64_t *); |
| #endif /* RESTORE */ |
| static char *sig_numstring(int num); |
| static char *strpbrkquotes(char *p, const char *sep); |
| |
| |
| /* definition of locally defined global variables ****************************/ |
| |
| char *progname = 0; /* used in all error output */ |
| char *homedir = 0; /* directory invoked from */ |
| bool_t pipeline = BOOL_FALSE; |
| bool_t stdoutpiped = BOOL_FALSE; |
| pthread_t parenttid; |
| char *sistr; |
| size_t pgsz; |
| size_t pgmask; |
| |
| |
| /* definition of locally defined static variables *****************************/ |
| |
| static rlim64_t minstacksz; |
| static rlim64_t maxstacksz; |
| #ifdef RESTORE |
| static size64_t vmsz; |
| #endif /* RESTORE */ |
| static time32_t stop_deadline; |
| static bool_t stop_in_progress; |
| static bool_t sighup_received; |
| static bool_t sigterm_received; |
| static bool_t sigquit_received; |
| static bool_t sigint_received; |
| /* REFERENCED */ |
| static int sigstray_received; |
| static bool_t progrpt_enabledpr; |
| static time32_t progrpt_interval; |
| static time32_t progrpt_deadline; |
| |
| |
| /* definition of locally defined global functions ****************************/ |
| |
| |
| int |
| main(int argc, char *argv[]) |
| { |
| int c; |
| #ifdef DUMP |
| uid_t euid; |
| #endif /* DUMP */ |
| ix_t stix; /* stream index */ |
| bool_t infoonly; |
| #ifdef DUMP |
| global_hdr_t *gwhdrtemplatep; |
| #endif /* DUMP */ |
| bool_t init_error; |
| bool_t coredump_requested = BOOL_FALSE; |
| int exitcode; |
| rlim64_t tmpstacksz; |
| struct sigaction sa; |
| int prbcld_xc = EXIT_NORMAL; |
| int xc; |
| bool_t ok; |
| /* REFERENCED */ |
| int rval; |
| int err; |
| |
| /* sanity checks |
| */ |
| assert(sizeof(char_t) == 1); |
| assert(sizeof(u_char_t) == 1); |
| assert(sizeof(int32_t) == 4); |
| assert(sizeof(uint32_t) == 4); |
| assert(sizeof(size32_t) == 4); |
| assert(sizeof(int64_t) == 8); |
| assert(sizeof(uint64_t) == 8); |
| assert(sizeof(size64_t) == 8); |
| |
| /* record the command name used to invoke |
| */ |
| progname = argv[0]; |
| |
| /* setup I18N support */ |
| setlocale(LC_ALL, ""); |
| bindtextdomain(PACKAGE, LOCALEDIR); |
| textdomain(PACKAGE); |
| |
| /* bootstrap message logging (stage 0) |
| */ |
| mlog_init0(); |
| |
| /* Get the parent's pthread id. will be used |
| * to differentiate parent from children. |
| */ |
| parenttid = pthread_self(); |
| rval = atexit(mlog_exit_flush); |
| assert(rval == 0); |
| |
| /* pre-scan the command line for the option file option. |
| * if found, create a new argv. |
| */ |
| ok = loadoptfile(&argc, &argv); |
| if (!ok) { |
| return mlog_exit(EXIT_ERROR, RV_OPT); |
| } |
| |
| /* initialize message logging (stage 1) |
| */ |
| ok = mlog_init1(argc, argv); |
| if (!ok) { |
| return mlog_exit(EXIT_ERROR, RV_INIT); |
| } |
| /* scan the command line for the info, progress |
| * report options, and stacksz. |
| */ |
| minstacksz = MINSTACKSZ; |
| maxstacksz = MAXSTACKSZ; |
| infoonly = BOOL_FALSE; |
| progrpt_enabledpr = BOOL_FALSE; |
| optind = 1; |
| opterr = 0; |
| while ((c = getopt(argc, argv, GETOPT_CMDSTRING)) != EOF) { |
| switch (c) { |
| case GETOPT_MINSTACKSZ: |
| if (!optarg || optarg[0] == '-') { |
| mlog(MLOG_NORMAL | MLOG_ERROR | MLOG_NOLOCK, |
| _("-%c argument missing\n"), |
| c); |
| usage(); |
| return mlog_exit(EXIT_ERROR, RV_OPT); |
| } |
| errno = 0; |
| tmpstacksz = strtoull(optarg, 0, 0); |
| if (tmpstacksz == UINT64_MAX |
| || |
| errno == ERANGE) { |
| mlog(MLOG_NORMAL | MLOG_ERROR | MLOG_NOLOCK, |
| _("-%c argument (%s) invalid\n"), |
| c, |
| optarg); |
| usage(); |
| return mlog_exit(EXIT_ERROR, RV_OPT); |
| } |
| minstacksz = tmpstacksz; |
| break; |
| case GETOPT_MAXSTACKSZ: |
| if (!optarg || optarg[0] == '-') { |
| mlog(MLOG_NORMAL | MLOG_ERROR | MLOG_NOLOCK, |
| _("-%c argument missing\n"), |
| c); |
| usage(); |
| return mlog_exit(EXIT_ERROR, RV_OPT); |
| } |
| errno = 0; |
| tmpstacksz = strtoull(optarg, 0, 0); |
| if (tmpstacksz == UINT64_MAX |
| || |
| errno == ERANGE) { |
| mlog(MLOG_NORMAL | MLOG_ERROR | MLOG_NOLOCK, |
| _("-%c argument (%s) invalid\n"), |
| c, |
| optarg); |
| usage(); |
| return mlog_exit(EXIT_ERROR, RV_OPT); |
| } |
| maxstacksz = tmpstacksz; |
| break; |
| case GETOPT_HELP: |
| infoonly = BOOL_TRUE; |
| mlog_exit_hint(RV_USAGE); |
| break; |
| case GETOPT_PROGRESS: |
| if (!optarg || optarg[0] == '-') { |
| mlog(MLOG_NORMAL | MLOG_ERROR | MLOG_NOLOCK, |
| _("-%c argument missing\n"), |
| c); |
| usage(); |
| return mlog_exit(EXIT_ERROR, RV_OPT); |
| } |
| progrpt_interval = (time32_t)atoi(optarg); |
| if (progrpt_interval > 0) { |
| progrpt_enabledpr = BOOL_TRUE; |
| } else { |
| progrpt_enabledpr = BOOL_FALSE; |
| } |
| break; |
| } |
| } |
| |
| /* sanity check resultant stack size limits |
| */ |
| if (minstacksz > maxstacksz) { |
| mlog(MLOG_NORMAL |
| | |
| MLOG_ERROR |
| | |
| MLOG_NOLOCK |
| | |
| MLOG_PROC, |
| _("specified minimum stack size is larger than maximum: " |
| "min is 0x%llx, max is 0x%llx\n"), |
| minstacksz, |
| maxstacksz); |
| return mlog_exit(EXIT_ERROR, RV_INIT); |
| } |
| |
| if (argc == 1) { |
| infoonly = BOOL_TRUE; |
| } |
| |
| /* set a progress report deadline to allow preemptchk() to |
| * report |
| */ |
| if (progrpt_enabledpr) { |
| progrpt_deadline = time(0) + progrpt_interval; |
| } |
| |
| /* intitialize the stream manager |
| */ |
| stream_init(); |
| |
| #ifdef DUMP |
| /* set the memory limits to their appropriate values. |
| */ |
| ok = set_rlimits(); |
| #endif /* DUMP */ |
| #ifdef RESTORE |
| /* set the memory limits to their appropriate values. this is necessary |
| * to accomodate the tree abstraction and some recursive functions. |
| * also determines maximum vm, which will be budgeted among the |
| * various abstractions. |
| */ |
| ok = set_rlimits(&vmsz); |
| #endif /* RESTORE */ |
| if (!ok) { |
| return mlog_exit(EXIT_ERROR, RV_INIT); |
| } |
| |
| /* initialize message logging (stage 2) - allocate the message lock |
| */ |
| ok = mlog_init2(); |
| if (!ok) { |
| return mlog_exit(EXIT_ERROR, RV_INIT); |
| } |
| |
| /* initialize the critical region lock |
| */ |
| lock_init(); |
| rmt_turnonmsgs(1); /* turn on WARNING msgs for librmt */ |
| |
| mlog(MLOG_NITTY + 1, "INTGENMAX == %ld (0x%lx)\n", INTGENMAX, INTGENMAX); |
| mlog(MLOG_NITTY + 1, "UINTGENMAX == %lu (0x%lx)\n", UINTGENMAX, UINTGENMAX); |
| mlog(MLOG_NITTY + 1, "OFF64MAX == %lld (0x%llx)\n", OFF64MAX, OFF64MAX); |
| mlog(MLOG_NITTY + 1, "OFFMAX == %ld (0x%lx)\n", OFFMAX, OFFMAX); |
| mlog(MLOG_NITTY + 1, "SIZEMAX == %lu (0x%lx)\n", SIZEMAX, SIZEMAX); |
| mlog(MLOG_NITTY + 1, "INOMAX == %lu (0x%lx)\n", INOMAX, INOMAX); |
| mlog(MLOG_NITTY + 1, "TIMEMAX == %ld (0x%lx)\n", TIMEMAX, TIMEMAX); |
| mlog(MLOG_NITTY + 1, "SIZE64MAX == %llu (0x%llx)\n", SIZE64MAX, SIZE64MAX); |
| mlog(MLOG_NITTY + 1, "INO64MAX == %llu (0x%llx)\n", INO64MAX, INO64MAX); |
| mlog(MLOG_NITTY + 1, "UINT64MAX == %llu (0x%llx)\n", UINT64MAX, UINT64MAX); |
| mlog(MLOG_NITTY + 1, "INT64MAX == %lld (0x%llx)\n", INT64MAX, INT64MAX); |
| mlog(MLOG_NITTY + 1, "UINT32MAX == %u (0x%x)\n", UINT32MAX, UINT32MAX); |
| mlog(MLOG_NITTY + 1, "INT32MAX == %d (0x%x)\n", INT32MAX, INT32MAX); |
| mlog(MLOG_NITTY + 1, "INT16MAX == %d (0x%x)\n", INT16MAX, INT16MAX); |
| mlog(MLOG_NITTY + 1, "UINT16MAX == %u (0x%x)\n", UINT16MAX, UINT16MAX); |
| |
| /* ask the system for the true vm page size, which must be used |
| * in all mmap calls |
| */ |
| pgsz = (size_t)getpagesize(); |
| mlog(MLOG_DEBUG | MLOG_PROC, |
| "getpagesize( ) returns %u\n", |
| pgsz); |
| assert((int)pgsz > 0); |
| pgmask = pgsz - 1; |
| |
| /* report parent tid |
| */ |
| mlog(MLOG_DEBUG | MLOG_PROC, |
| "parent tid is %lu\n", |
| parenttid); |
| |
| /* get the current working directory: this is where we will dump |
| * core, if necessary. some tmp files may be placed here as well. |
| */ |
| homedir = getcwd(0, MAXPATHLEN); |
| if (!homedir) { |
| mlog(MLOG_NORMAL | MLOG_ERROR, |
| _("unable to determine current directory: %s\n"), |
| strerror(errno)); |
| return mlog_exit(EXIT_ERROR, RV_INIT); |
| } |
| |
| /* sanity check the inventory database directory, setup global paths |
| */ |
| ok = inv_setup_base(); |
| if (!ok) { |
| mlog(MLOG_NORMAL | MLOG_ERROR | MLOG_NOLOCK, |
| _("both /var/lib/xfsdump and /var/xfsdump exist - fatal\n")); |
| return mlog_exit(EXIT_ERROR, RV_INIT); |
| } |
| |
| /* if just looking for info, oblige |
| */ |
| if (infoonly) { |
| mlog(MLOG_NORMAL, |
| _("version %s (dump format %d.0)\n"), |
| VERSION, GLOBAL_HDR_VERSION); |
| usage(); |
| return mlog_exit(EXIT_NORMAL, RV_OK); /* normal termination */ |
| } |
| |
| /* if an inventory display is requested, do it and exit |
| */ |
| if (!inv_DEBUG_print(argc, argv)) { |
| return mlog_exit(EXIT_NORMAL, RV_OK); /* normal termination */ |
| } |
| |
| #ifdef DUMP |
| /* insist that the effective user id is root. |
| * this must appear after inv_DEBUG_print(), |
| * so it may be done without root privilege. |
| */ |
| euid = geteuid(); |
| mlog(MLOG_DEBUG | MLOG_PROC, |
| "effective user id is %d\n", |
| euid); |
| if (euid != 0) { |
| mlog(MLOG_NORMAL, |
| _("effective user ID must be root\n")); |
| return mlog_exit(EXIT_ERROR, RV_PERM); |
| } |
| #endif /* DUMP */ |
| |
| /* initialize operator dialog capability |
| */ |
| ok = dlog_init(argc, argv); |
| if (!ok) { |
| return mlog_exit(EXIT_ERROR, RV_INIT); |
| } |
| |
| /* initialize the child process manager |
| */ |
| ok = cldmgr_init(); |
| if (!ok) { |
| return mlog_exit(EXIT_ERROR, RV_INIT); |
| } |
| |
| /* select and instantiate a drive manager for each stream. this |
| * is the first pass at initialization, so don't do anything |
| * terribly time-consuming here. A second initialization pass |
| * will be done shortly. |
| */ |
| ok = drive_init1(argc, argv); |
| if (!ok) { |
| return mlog_exit(EXIT_ERROR, RV_INIT); |
| } |
| |
| /* check the drives to see if we're in a pipeline. |
| * if not, check stdout anyway, in case someone is trying to pipe |
| * the log messages into more, tee, ... |
| */ |
| if (drivepp[0]->d_isunnamedpipepr) { |
| mlog(MLOG_DEBUG | MLOG_NOTE, |
| "pipeline detected\n"); |
| pipeline = BOOL_TRUE; |
| } else { |
| struct stat64 statbuf; |
| if (fstat64(1, &statbuf) == 0 |
| && |
| (statbuf.st_mode & S_IFMT) == S_IFIFO) { |
| stdoutpiped = BOOL_TRUE; |
| } |
| } |
| |
| /* announce version and instructions |
| */ |
| sistr = sigintstr(); |
| mlog(MLOG_VERBOSE, |
| _("version %s (dump format %d.0)"), |
| VERSION, GLOBAL_HDR_VERSION); |
| if (!pipeline && !stdoutpiped && sistr && dlog_allowed()) { |
| mlog(MLOG_VERBOSE | MLOG_BARE, _( |
| " - " |
| "type %s for status and control\n"), |
| sistr); |
| } else { |
| mlog(MLOG_VERBOSE | MLOG_BARE, |
| "\n"); |
| } |
| |
| #ifdef DUMP |
| /* build a global write header template |
| */ |
| gwhdrtemplatep = global_hdr_alloc(argc, argv); |
| if (!gwhdrtemplatep) { |
| return mlog_exit(EXIT_ERROR, RV_INIT); |
| } |
| #endif /* DUMP */ |
| |
| /* tell mlog how many streams there are. the format of log messages |
| * depends on whether there are one or many. |
| */ |
| mlog_tell_streamcnt(drivecnt); |
| |
| /* initialize the state of signal processing. if in a pipeline, just |
| * want to exit when a signal is received. otherwise, hold signals so |
| * they don't interfere with sys calls; they will be released at |
| * pre-emption points and upon pausing in the main loop. |
| * |
| * note that since we're multi-threaded, handling SIGCHLD causes |
| * problems with system()'s ability to obtain a child's exit status |
| * (because the main thread may process SIGCHLD before the thread |
| * running system() calls waitpid()). likewise explicitly ignoring |
| * SIGCHLD also prevents system() from getting an exit status. |
| * therefore we don't do anything with SIGCHLD. |
| */ |
| |
| sigfillset(&sa.sa_mask); |
| sa.sa_flags = 0; |
| |
| /* always ignore SIGPIPE, instead handle EPIPE as part |
| * of normal sys call error handling. |
| */ |
| sa.sa_handler = SIG_IGN; |
| sigaction(SIGPIPE, &sa, NULL); |
| |
| if (!pipeline) { |
| sigset_t blocked_set; |
| |
| stop_in_progress = BOOL_FALSE; |
| coredump_requested = BOOL_FALSE; |
| sighup_received = BOOL_FALSE; |
| sigterm_received = BOOL_FALSE; |
| sigint_received = BOOL_FALSE; |
| sigquit_received = BOOL_FALSE; |
| sigstray_received = BOOL_FALSE; |
| |
| alarm(0); |
| |
| sigemptyset(&blocked_set); |
| sigaddset(&blocked_set, SIGINT); |
| sigaddset(&blocked_set, SIGHUP); |
| sigaddset(&blocked_set, SIGTERM); |
| sigaddset(&blocked_set, SIGQUIT); |
| sigaddset(&blocked_set, SIGALRM); |
| sigaddset(&blocked_set, SIGUSR1); |
| pthread_sigmask(SIG_SETMASK, &blocked_set, NULL); |
| |
| sa.sa_handler = sighandler; |
| sigaction(SIGINT, &sa, NULL); |
| sigaction(SIGHUP, &sa, NULL); |
| sigaction(SIGTERM, &sa, NULL); |
| sigaction(SIGQUIT, &sa, NULL); |
| sigaction(SIGALRM, &sa, NULL); |
| sigaction(SIGUSR1, &sa, NULL); |
| } |
| |
| /* do content initialization. |
| */ |
| #ifdef DUMP |
| ok = content_init(argc, argv, gwhdrtemplatep); |
| #endif /* DUMP */ |
| #ifdef RESTORE |
| ok = content_init(argc, argv, vmsz / VMSZ_PER); |
| #endif /* RESTORE */ |
| if (!ok) { |
| err = mlog_exit(EXIT_ERROR, RV_INIT); |
| goto err_free; |
| } |
| |
| /* if in a pipeline, go single-threaded with just one stream. |
| */ |
| if (pipeline) { |
| int exitcode; |
| |
| sa.sa_handler = sighandler; |
| sigaction(SIGINT, &sa, NULL); |
| sigaction(SIGHUP, &sa, NULL); |
| sigaction(SIGTERM, &sa, NULL); |
| sigaction(SIGQUIT, &sa, NULL); |
| |
| #ifdef DUMP |
| ok = drive_init2(argc, |
| argv, |
| gwhdrtemplatep); |
| #endif /* DUMP */ |
| #ifdef RESTORE |
| ok = drive_init2(argc, |
| argv, |
| (global_hdr_t *)0); |
| #endif /* RESTORE */ |
| if (!ok) { |
| err = mlog_exit(EXIT_ERROR, RV_INIT); |
| goto err_free; |
| } |
| ok = drive_init3(); |
| if (!ok) { |
| err = mlog_exit(EXIT_ERROR, RV_INIT); |
| goto err_free; |
| } |
| #ifdef DUMP |
| exitcode = content_stream_dump(0); |
| #endif /* DUMP */ |
| #ifdef RESTORE |
| exitcode = content_stream_restore(0); |
| #endif /* RESTORE */ |
| if (exitcode != EXIT_NORMAL) { |
| (void)content_complete(); |
| /* for cleanup side-effect */ |
| err = mlog_exit(exitcode, RV_UNKNOWN); |
| } else if (content_complete()) { |
| err = mlog_exit(EXIT_NORMAL, RV_OK); |
| } else { |
| err = mlog_exit(EXIT_INTERRUPT, RV_UNKNOWN); |
| } |
| goto err_free; |
| } |
| |
| /* used to skip to end if errors occur during any |
| * stage of initialization. |
| */ |
| init_error = BOOL_FALSE; |
| |
| /* now do the second and third passes of drive initialization. |
| * allocate per-stream write and read headers. if a drive |
| * manager uses a slave process, it should be created now, |
| * using cldmgr_create(). each drive manager may use the slave to |
| * asynchronously read the media file header, typically a very |
| * time-consuming chore. drive_init3 will synchronize with each slave. |
| */ |
| if (!init_error) { |
| #ifdef DUMP |
| ok = drive_init2(argc, |
| argv, |
| gwhdrtemplatep); |
| #endif /* DUMP */ |
| #ifdef RESTORE |
| ok = drive_init2(argc, |
| argv, |
| (global_hdr_t *)0); |
| #endif /* RESTORE */ |
| if (!ok) { |
| init_error = BOOL_TRUE; |
| } |
| } |
| if (!init_error) { |
| ok = drive_init3(); |
| if (!ok) { |
| init_error = BOOL_TRUE; |
| } |
| } |
| |
| /* create a child thread for each stream. drivecnt global from |
| * drive.h, initialized by drive_init[12] |
| */ |
| if (!init_error) { |
| for (stix = 0; stix < drivecnt; stix++) { |
| ok = cldmgr_create(childmain, |
| stix, |
| "child", |
| (void *)stix); |
| if (!ok) { |
| init_error = BOOL_TRUE; |
| } |
| } |
| } |
| |
| /* loop here, waiting for children to die, processing operator |
| * signals. |
| */ |
| if (progrpt_enabledpr) { |
| (void)alarm((uint)progrpt_interval); |
| } |
| for (;;) { |
| time32_t now; |
| bool_t stop_requested = BOOL_FALSE; |
| int stop_timeout = -1; |
| sigset_t empty_set; |
| |
| /* if there was an initialization error, |
| * immediately stop all children. |
| */ |
| if (init_error) { |
| stop_timeout = STOP_TIMEOUT; |
| stop_requested = BOOL_TRUE; |
| } |
| |
| /* if one or more children died abnormally, request a |
| * stop. furthermore, note that core should be dumped if |
| * the child explicitly exited with EXIT_FAULT. |
| */ |
| xc = cldmgr_join(); |
| if (xc) { |
| if (xc == EXIT_FAULT) { |
| coredump_requested = BOOL_TRUE; |
| stop_timeout = ABORT_TIMEOUT; |
| } else { |
| stop_timeout = STOP_TIMEOUT; |
| } |
| prbcld_xc = xc; |
| stop_requested = BOOL_TRUE; |
| } |
| |
| /* all children died normally. break out. |
| */ |
| if (cldmgr_remainingcnt() == 0) { |
| mlog(MLOG_DEBUG, |
| "all children have exited\n"); |
| break; |
| } |
| |
| /* get the current time |
| */ |
| now = time(0); |
| |
| /* check for stop timeout. request a core dump and bail |
| */ |
| if (stop_in_progress && now >= stop_deadline) { |
| mlog(MLOG_NORMAL | MLOG_ERROR, |
| _("session interrupt timeout\n")); |
| coredump_requested = BOOL_TRUE; |
| break; |
| } |
| |
| /* operator sent SIGINT. if dialog allowed, enter dialog. |
| * otherwise treat as a hangup and request a stop. |
| */ |
| if (sigint_received) { |
| mlog(MLOG_DEBUG | MLOG_PROC, |
| "SIGINT received\n"); |
| if (stop_in_progress) { |
| if (dlog_allowed()) { |
| (void)sigint_dialog(); |
| } |
| /* |
| mlog(MLOG_NORMAL, |
| _("session interrupt in progress: " |
| "please wait\n")); |
| */ |
| } else { |
| if (dlog_allowed()) { |
| stop_requested = sigint_dialog(); |
| } else { |
| stop_requested = BOOL_TRUE; |
| } |
| stop_timeout = STOP_TIMEOUT; |
| } |
| |
| /* important that this appear after dialog. |
| * allows dialog to be terminated with SIGINT, |
| * without infinite loop. |
| */ |
| sigint_received = BOOL_FALSE; |
| } |
| |
| /* refresh the current time in case in dialog for a while |
| */ |
| now = time(0); |
| |
| /* request a stop on hangup |
| */ |
| if (sighup_received) { |
| mlog(MLOG_DEBUG | MLOG_PROC, |
| "SIGHUP received\n"); |
| stop_requested = BOOL_TRUE; |
| stop_timeout = STOP_TIMEOUT; |
| sighup_received = BOOL_FALSE; |
| } |
| |
| /* request a stop on termination request |
| */ |
| if (sigterm_received) { |
| mlog(MLOG_DEBUG | MLOG_PROC, |
| "SIGTERM received\n"); |
| stop_requested = BOOL_TRUE; |
| stop_timeout = STOP_TIMEOUT; |
| sigterm_received = BOOL_FALSE; |
| } |
| |
| /* operator send SIGQUIT. treat like an interrupt, |
| * but force a core dump |
| */ |
| if (sigquit_received) { |
| mlog(MLOG_NORMAL | MLOG_PROC, |
| "SIGQUIT received\n"); |
| if (stop_in_progress) { |
| mlog(MLOG_NORMAL, |
| _("session interrupt in progress: " |
| "please wait\n")); |
| stop_deadline = now; |
| } else { |
| stop_requested = BOOL_TRUE; |
| stop_timeout = ABORT_TIMEOUT; |
| sigquit_received = BOOL_FALSE; |
| coredump_requested = BOOL_TRUE; |
| } |
| } |
| |
| /* see if need to initiate a stop |
| */ |
| if (stop_requested && !stop_in_progress) { |
| mlog(MLOG_NORMAL, |
| _("initiating session interrupt (timeout in %d sec)\n"), |
| stop_timeout); |
| mlog_exit_hint(RV_INTR); |
| stop_in_progress = BOOL_TRUE; |
| cldmgr_stop(); |
| assert(stop_timeout >= 0); |
| stop_deadline = now + (time32_t)stop_timeout; |
| } |
| |
| /* set alarm if needed (note time stands still during dialog) |
| */ |
| if (stop_in_progress) { |
| int timeout = (int)(stop_deadline - now); |
| if (timeout < 0) { |
| timeout = 0; |
| } |
| mlog(MLOG_DEBUG | MLOG_PROC, |
| "setting alarm for %d second%s\n", |
| timeout, |
| timeout == 1 ? "" : "s"); |
| (void)alarm((uint)timeout); |
| if (timeout == 0) { |
| continue; |
| } |
| } |
| |
| if (progrpt_enabledpr && !stop_in_progress) { |
| bool_t need_progrptpr = BOOL_FALSE; |
| while (now >= progrpt_deadline) { |
| need_progrptpr = BOOL_TRUE; |
| progrpt_deadline += progrpt_interval; |
| } |
| if (need_progrptpr) { |
| size_t statlinecnt; |
| char **statline; |
| ix_t i; |
| statlinecnt = content_statline(&statline); |
| for (i = 0; i < statlinecnt; i++) { |
| mlog(MLOG_NORMAL, |
| statline[i]); |
| } |
| } |
| (void)alarm((uint)(progrpt_deadline |
| - |
| now)); |
| } |
| |
| /* sleep until next signal |
| */ |
| sigemptyset(&empty_set); |
| sigsuspend(&empty_set); |
| (void)alarm(0); |
| } |
| |
| /* check if core dump requested |
| */ |
| if (coredump_requested) { |
| mlog(MLOG_DEBUG | MLOG_PROC, |
| "core dump requested, aborting (pid %d)\n", |
| getpid()); |
| abort(); |
| } |
| |
| /* determine if dump or restore was interrupted |
| * or an initialization error occurred. |
| */ |
| if (init_error) { |
| (void)content_complete(); |
| exitcode = EXIT_ERROR; |
| } else { |
| if (content_complete()) { |
| if (prbcld_xc != EXIT_NORMAL) |
| exitcode = EXIT_ERROR; |
| else |
| exitcode = EXIT_NORMAL; |
| } else { |
| exitcode = EXIT_INTERRUPT; |
| if (mlog_get_hint() == RV_NONE) |
| mlog_exit_hint(RV_INCOMPLETE); |
| } |
| } |
| |
| err = mlog_exit(exitcode, RV_UNKNOWN); |
| |
| err_free: |
| #ifdef DUMP |
| global_hdr_free(gwhdrtemplatep); |
| #endif /* DUMP */ |
| return err; |
| } |
| |
| #define ULO(f, o) fprintf(stderr, \ |
| "%*s[ -%c %s ]\n", \ |
| ps, \ |
| ns, \ |
| o, \ |
| f), \ |
| ps = pfxsz |
| |
| #define ULN(f) fprintf(stderr, \ |
| "%*s[ %s ]\n", \ |
| ps, \ |
| ns, \ |
| f), \ |
| ps = pfxsz |
| |
| void |
| usage(void) |
| { |
| int pfxsz; |
| int ps = 0; |
| char *ns = ""; |
| |
| pfxsz = fprintf(stderr, _("%s: usage: %s "), |
| progname, basename(progname)); |
| |
| #ifdef DUMP |
| ULO(_("(dump DMF dualstate files as offline)"), GETOPT_DUMPASOFFLINE); |
| ULO(_("<blocksize>"), GETOPT_BLOCKSIZE); |
| ULO(_("<media change alert program> "), GETOPT_ALERTPROG); |
| ULO(_("<dump media file size> "), GETOPT_FILESZ); |
| ULO(_("(allow files to be excluded)"), GETOPT_EXCLUDEFILES); |
| ULO(_("<destination> ..."), GETOPT_DUMPDEST); |
| ULO(_("(help)"), GETOPT_HELP); |
| ULO(_("<level>"), GETOPT_LEVEL); |
| ULO(_("(force usage of minimal rmt)"), GETOPT_MINRMT); |
| ULO(_("(overwrite tape)"), GETOPT_OVERWRITE); |
| ULO(_("<seconds between progress reports>"), GETOPT_PROGRESS); |
| ULO(_("<use QIC tape settings>"), GETOPT_QIC); |
| ULO(_("<subtree> ..."), GETOPT_SUBTREE); |
| ULO(_("<file> (use file mtime for dump time"), GETOPT_DUMPTIME); |
| ULO(_("<verbosity {silent, verbose, trace}>"), GETOPT_VERBOSITY); |
| ULO(_("<maximum file size>"), GETOPT_MAXDUMPFILESIZE); |
| ULO(_("(don't dump extended file attributes)"), GETOPT_NOEXTATTR); |
| ULO(_("<base dump session id>"), GETOPT_BASED); |
| #ifdef REVEAL |
| ULO(_("(generate tape record checksums)"), GETOPT_RECCHKSUM); |
| #endif /* REVEAL */ |
| ULO(_("(skip unchanged directories)"), GETOPT_NOUNCHANGEDDIRS); |
| ULO(_("(pre-erase media)"), GETOPT_ERASE); |
| ULO(_("(don't prompt)"), GETOPT_FORCE); |
| #ifdef REVEAL |
| ULO(_("<minimum thread stack size>"), GETOPT_MINSTACKSZ); |
| ULO(_("<maximum thread stack size>"), GETOPT_MAXSTACKSZ); |
| #endif /* REVEAL */ |
| ULO(_("(display dump inventory)"), GETOPT_INVPRINT); |
| ULO(_("(inhibit inventory update)"), GETOPT_NOINVUPDATE); |
| ULO(_("(generate format 2 dump)"), GETOPT_FMT2COMPAT); |
| ULO(_("<session label>"), GETOPT_DUMPLABEL); |
| ULO(_("<media label> ..."), GETOPT_MEDIALABEL); |
| #ifdef REVEAL |
| ULO(_("(timestamp messages)"), GETOPT_TIMESTAMP); |
| #endif /* REVEAL */ |
| ULO(_("<options file>"), GETOPT_OPTFILE); |
| #ifdef REVEAL |
| ULO(_("(pin down I/O buffers)"), GETOPT_RINGPIN); |
| #endif /* REVEAL */ |
| ULO(_("(resume)"), GETOPT_RESUME); |
| ULO(_("(don't timeout dialogs)"), GETOPT_NOTIMEOUTS); |
| #ifdef REVEAL |
| ULO(_("(unload media when change needed)"), GETOPT_UNLOAD); |
| ULO(_("(show subsystem in messages)"), GETOPT_SHOWLOGSS); |
| ULO(_("(show verbosity in messages)"), GETOPT_SHOWLOGLEVEL); |
| #endif /* REVEAL */ |
| ULO(_("<I/O buffer ring length>"), GETOPT_RINGLEN); |
| ULN(_("- (stdout)")); |
| ULN(_("<source (mntpnt|device)>")); |
| #endif /* DUMP */ |
| #ifdef RESTORE |
| ULO(_("<alt. workspace dir> ..."), GETOPT_WORKSPACE); |
| ULO(_("<blocksize>"), GETOPT_BLOCKSIZE); |
| ULO(_("<media change alert program> "), GETOPT_ALERTPROG); |
| ULO(_("(don't overwrite existing files)"), GETOPT_EXISTING); |
| ULO(_("<source> ..."), GETOPT_DUMPDEST); |
| ULO(_("(help)"), GETOPT_HELP); |
| ULO(_("(interactive)"), GETOPT_INTERACTIVE); |
| ULO(_("(force usage of minimal rmt)"), GETOPT_MINRMT); |
| ULO(_("<file> (restore only if newer than)"), GETOPT_NEWER); |
| ULO(_("(restore owner/group even if not root)"),GETOPT_OWNER); |
| ULO(_("<seconds between progress reports>"), GETOPT_PROGRESS); |
| ULO(_("<use QIC tape settings>"), GETOPT_QIC); |
| ULO(_("(cumulative restore)"), GETOPT_CUMULATIVE); |
| ULO(_("<subtree> ..."), GETOPT_SUBTREE); |
| ULO(_("(contents only)"), GETOPT_TOC); |
| ULO(_("<verbosity {silent, verbose, trace}>"), GETOPT_VERBOSITY); |
| ULO(_("(use small tree window)"), GETOPT_SMALLWINDOW); |
| ULO(_("(don't restore extended file attributes)"), GETOPT_NOEXTATTR); |
| ULO(_("(restore root dir owner/permissions)"), GETOPT_ROOTPERM); |
| ULO(_("(restore DMAPI event settings)"), GETOPT_SETDM); |
| #ifdef REVEAL |
| ULO(_("(check tape record checksums)"), GETOPT_RECCHKSUM); |
| #endif /* REVEAL */ |
| ULO(_("(don't overwrite if changed)"), GETOPT_CHANGED); |
| ULO(_("(don't prompt)"), GETOPT_FORCE); |
| ULO(_("(display dump inventory)"), GETOPT_INVPRINT); |
| ULO(_("(inhibit inventory update)"), GETOPT_NOINVUPDATE); |
| ULO(_("(force use of format 2 generation numbers)"), GETOPT_FMT2COMPAT); |
| ULO(_("<session label>"), GETOPT_DUMPLABEL); |
| #ifdef REVEAL |
| ULO(_("(timestamp messages)"), GETOPT_TIMESTAMP); |
| #endif /* REVEAL */ |
| ULO(_("<options file>"), GETOPT_OPTFILE); |
| #ifdef REVEAL |
| ULO(_("(pin down I/O buffers)"), GETOPT_RINGPIN); |
| #endif /* REVEAL */ |
| ULO(_("(force interrupted session completion)"),GETOPT_SESSCPLT); |
| ULO(_("(resume)"), GETOPT_RESUME); |
| ULO(_("<session id>"), GETOPT_SESSIONID); |
| ULO(_("(don't timeout dialogs)"), GETOPT_NOTIMEOUTS); |
| #ifdef REVEAL |
| ULO(_("(unload media when change needed)"), GETOPT_UNLOAD); |
| ULO(_("(show subsystem in messages)"), GETOPT_SHOWLOGSS); |
| ULO(_("(show verbosity in messages)"), GETOPT_SHOWLOGLEVEL); |
| #endif /* REVEAL */ |
| ULO(_("<excluded subtree> ..."), GETOPT_NOSUBTREE); |
| ULO(_("<I/O buffer ring length>"), GETOPT_RINGLEN); |
| ULN(_("- (stdin)")); |
| ULN(_("<destination>")); |
| #endif /* RESTORE */ |
| |
| /* anywhere usage is called we will exit shortly after... |
| * catch all of those cases below |
| */ |
| |
| (void) mlog_exit(EXIT_ERROR, RV_OPT); |
| } |
| |
| /* returns TRUE if preemption |
| */ |
| bool_t |
| preemptchk(int flg) |
| { |
| bool_t preempt_requested; |
| int i; |
| int sigs[] = { SIGINT, SIGHUP, SIGTERM, SIGQUIT }; |
| int num_sigs = sizeof(sigs) / sizeof(sigs[0]); |
| sigset_t pending_set, handle_set; |
| |
| /* see if a progress report needed |
| */ |
| if (progrpt_enabledpr) { |
| time32_t now = time(0); |
| bool_t need_progrptpr = BOOL_FALSE; |
| while (now >= progrpt_deadline) { |
| need_progrptpr = BOOL_TRUE; |
| progrpt_deadline += progrpt_interval; |
| } |
| if (need_progrptpr) { |
| size_t statlinecnt; |
| char **statline; |
| ix_t i; |
| statlinecnt = content_statline(&statline); |
| for (i = 0; i < statlinecnt; i++) { |
| mlog(MLOG_NORMAL, |
| statline[i]); |
| } |
| } |
| } |
| |
| /* Progress report only */ |
| if (flg == PREEMPT_PROGRESSONLY) { |
| return BOOL_FALSE; |
| } |
| |
| /* signals not caught if in a pipeline |
| */ |
| if (pipeline) { |
| return BOOL_FALSE; |
| } |
| |
| /* release signals momentarily to let any pending ones |
| * invoke signal handler and set flags |
| */ |
| sigpending(&pending_set); |
| for (i = 0; i < num_sigs; i++) { |
| if (sigismember(&pending_set, sigs[i]) == 1) { |
| sigfillset(&handle_set); |
| sigdelset(&handle_set, sigs[i]); |
| sigsuspend(&handle_set); |
| } |
| } |
| |
| preempt_requested = BOOL_FALSE; |
| |
| if (sigint_received) { |
| mlog(MLOG_DEBUG | MLOG_PROC, |
| "SIGINT received (preempt)\n"); |
| if (dlog_allowed()) { |
| preempt_requested = sigint_dialog(); |
| } else { |
| preempt_requested = BOOL_TRUE; |
| } |
| /* important that this appear after dialog. |
| * allows dialog to be terminated with SIGINT, |
| * without infinite loop. |
| */ |
| sigint_received = BOOL_FALSE; |
| } |
| |
| if (sighup_received) { |
| mlog(MLOG_DEBUG | MLOG_PROC, |
| "SIGHUP received (prempt)\n"); |
| preempt_requested = BOOL_TRUE; |
| sighup_received = BOOL_FALSE; |
| } |
| |
| if (sigterm_received) { |
| mlog(MLOG_DEBUG | MLOG_PROC, |
| "SIGTERM received (prempt)\n"); |
| preempt_requested = BOOL_TRUE; |
| sigterm_received = BOOL_FALSE; |
| } |
| |
| if (sigquit_received) { |
| mlog(MLOG_DEBUG | MLOG_PROC, |
| "SIGQUIT received (preempt)\n"); |
| preempt_requested = BOOL_TRUE; |
| sigquit_received = BOOL_FALSE; |
| } |
| |
| return preempt_requested; |
| } |
| |
| /* definition of locally defined static functions ****************************/ |
| |
| static bool_t |
| loadoptfile(int *argcp, char ***argvp) |
| { |
| char *optfilename; |
| ix_t optfileix = 0; |
| int fd; |
| size_t sz; |
| int i; |
| struct stat64 stat; |
| char *argbuf; |
| char *p; |
| size_t tokencnt; |
| int nread; |
| const char *sep = " \t\n\r"; |
| char **newargv; |
| int c; |
| int rval; |
| |
| /* see if option specified |
| */ |
| optind = 1; |
| opterr = 0; |
| optfilename = 0; |
| while ((c = getopt(*argcp, *argvp, GETOPT_CMDSTRING)) != EOF) { |
| switch (c) { |
| case GETOPT_OPTFILE: |
| if (!optarg || optarg[0] == '-') { |
| mlog(MLOG_NORMAL | MLOG_ERROR | MLOG_NOLOCK, |
| _("-%c argument missing\n"), |
| c); |
| usage(); |
| return BOOL_FALSE; |
| } |
| if (optfilename) { |
| mlog(MLOG_NORMAL | MLOG_ERROR | MLOG_NOLOCK, |
| _("-%c allowed only once\n"), |
| c); |
| usage(); |
| return BOOL_FALSE; |
| } |
| optfilename = optarg; |
| assert(optind > 2); |
| optfileix = (ix_t)optind - 2; |
| break; |
| } |
| } |
| if (!optfilename) { |
| return BOOL_TRUE; |
| } |
| |
| /* attempt to open the option file |
| */ |
| errno = 0; |
| fd = open(optfilename, O_RDONLY); |
| if (fd < 0) { |
| mlog(MLOG_ERROR | MLOG_NOLOCK, |
| _("cannot open option file %s: %s (%d)\n"), |
| optfilename, |
| strerror(errno), |
| errno); |
| return BOOL_FALSE; |
| } |
| |
| /* get file status |
| */ |
| rval = fstat64(fd, &stat); |
| if (rval) { |
| mlog(MLOG_ERROR | MLOG_NOLOCK, |
| _("cannot stat option file %s: %s (%d)\n"), |
| optfilename, |
| strerror(errno), |
| errno); |
| close(fd); |
| return BOOL_FALSE; |
| } |
| |
| /* ensure the file is ordinary |
| */ |
| if ((stat.st_mode & S_IFMT) != S_IFREG) { |
| mlog(MLOG_ERROR | MLOG_NOLOCK, |
| _("given option file %s is not ordinary file\n"), |
| optfilename); |
| close(fd); |
| return BOOL_FALSE; |
| } |
| |
| /* calculate the space required for the cmd line options. |
| * skip the GETOPT_OPTFILE option which put us here! |
| */ |
| sz = 0; |
| for (i = 0; i < *argcp; i++) { |
| if (i == (int)optfileix) { |
| i++; /* to skip option argument */ |
| continue; |
| } |
| sz += strlen((*argvp)[i]) + 1; |
| } |
| |
| /* add in the size of the option file (plus one byte in case |
| * option file ends without newline, and one NULL for safety) |
| */ |
| sz += (size_t)stat.st_size + 2; |
| |
| /* allocate an argument buffer |
| */ |
| argbuf = (char *)malloc(sz); |
| assert(argbuf); |
| |
| /* copy arg0 (the executable's name) in first |
| */ |
| p = argbuf; |
| i = 0; |
| sprintf(p, "%s ", ( * argvp)[ i]); |
| p += strlen((*argvp)[i]) + 1; |
| i++; |
| |
| /* copy the options file into the buffer after the given args |
| */ |
| nread = read(fd, (void *)p, (size_t)stat.st_size); |
| if (nread < 0) { |
| mlog(MLOG_ERROR | MLOG_NOLOCK, |
| _("read of option file %s failed: %s (%d)\n"), |
| optfilename, |
| strerror(errno), |
| errno); |
| close(fd); |
| return BOOL_FALSE; |
| } |
| assert((off64_t)nread == stat.st_size); |
| p += (size_t)stat.st_size; |
| *p++ = ' '; |
| |
| /* copy the remaining command line args into the buffer |
| */ |
| for (; i < *argcp; i++) { |
| if (i == (int)optfileix) { |
| i++; /* to skip option argument */ |
| continue; |
| } |
| sprintf(p, "%s ", ( * argvp)[ i]); |
| p += strlen((*argvp)[i]) + 1; |
| } |
| |
| /* null-terminate the entire buffer |
| */ |
| *p++ = 0; |
| assert((size_t)(p - argbuf) <= sz); |
| |
| /* change newlines and carriage returns into spaces |
| */ |
| for (p = argbuf; *p; p++) { |
| if (strchr("\n\r", ( int)( *p))) { |
| *p = ' '; |
| } |
| } |
| |
| /* count the tokens in the buffer |
| */ |
| tokencnt = 0; |
| p = argbuf; |
| for (;;) { |
| /* start at the first non-separator character |
| */ |
| while (*p && strchr(sep, (int)(*p))) { |
| p++; |
| } |
| |
| /* done when NULL encountered |
| */ |
| if (!*p) { |
| break; |
| } |
| |
| /* we have a token |
| */ |
| tokencnt++; |
| |
| /* find the end of the first token |
| */ |
| p = strpbrkquotes(p, sep); |
| |
| /* if no more separators, all tokens seen |
| */ |
| if (!p) { |
| break; |
| } |
| } |
| |
| /* if no arguments, can return now |
| */ |
| if (!tokencnt) { |
| close(fd); |
| return BOOL_TRUE; |
| } |
| |
| /* allocate a new argv array to hold the tokens |
| */ |
| newargv = (char **)calloc(tokencnt, sizeof(char *)); |
| assert(newargv); |
| |
| /* null-terminate tokens and place in new argv, after |
| * extracting quotes and escapes |
| */ |
| p = argbuf; |
| for (i = 0 ; ; i++) { |
| char *endp = 0; |
| |
| /* start at the first non-separator character |
| */ |
| while (*p && strchr(sep, (int)*p)) { |
| p++; |
| } |
| |
| /* done when NULL encountered |
| */ |
| if (!*p) { |
| break; |
| } |
| |
| /* better not disagree with counting scan! |
| */ |
| assert(i < (int)tokencnt); |
| |
| /* find the end of the first token |
| */ |
| endp = strpbrkquotes(p, sep); |
| |
| /* null-terminate if needed |
| */ |
| if (endp) { |
| *endp = 0; |
| } |
| |
| /* strip quotes and escapes |
| */ |
| p = stripquotes(p); |
| |
| /* stick result in new argv array |
| */ |
| newargv[i] = p; |
| |
| /* if no more separators, all tokens seen |
| */ |
| if (!endp) { |
| break; |
| } |
| |
| p = endp + 1; |
| } |
| |
| /* return new argc anr argv |
| */ |
| close(fd); |
| *argcp = (int)tokencnt; |
| *argvp = newargv; |
| return BOOL_TRUE; |
| } |
| |
| /* parent and children share this handler. |
| */ |
| static void |
| sighandler(int signo) |
| { |
| /* dialog gets first crack at the signal |
| */ |
| if (dlog_sighandler(signo)) |
| return; |
| |
| /* if in pipeline, don't do anything risky. just quit. |
| */ |
| if (pipeline) { |
| int rval; |
| |
| mlog(MLOG_TRACE | MLOG_NOTE | MLOG_NOLOCK | MLOG_PROC, |
| _("received signal %d (%s): cleanup and exit\n"), |
| signo, |
| sig_numstring(signo)); |
| |
| if (content_complete()) { |
| rval = EXIT_NORMAL; |
| } else { |
| rval = EXIT_INTERRUPT; |
| } |
| mlog_exit(rval, RV_NONE); |
| exit(rval); |
| } |
| |
| switch (signo) { |
| case SIGHUP: |
| /* immediately disable further dialogs |
| */ |
| dlog_desist(); |
| sighup_received = BOOL_TRUE; |
| break; |
| case SIGTERM: |
| /* immediately disable further dialogs |
| */ |
| dlog_desist(); |
| sigterm_received = BOOL_TRUE; |
| break; |
| case SIGINT: |
| sigint_received = BOOL_TRUE; |
| break; |
| case SIGQUIT: |
| /* immediately disable further dialogs |
| */ |
| dlog_desist(); |
| sigquit_received = BOOL_TRUE; |
| break; |
| case SIGALRM: |
| case SIGUSR1: |
| break; |
| default: |
| sigstray_received = signo; |
| break; |
| } |
| } |
| |
| static int |
| childmain(void *arg1) |
| { |
| ix_t stix; |
| int exitcode; |
| drive_t *drivep; |
| |
| /* Determine which stream I am. |
| */ |
| stix = (ix_t)arg1; |
| |
| /* tell the content manager to begin. |
| */ |
| #ifdef DUMP |
| exitcode = content_stream_dump(stix); |
| #endif /* DUMP */ |
| #ifdef RESTORE |
| exitcode = content_stream_restore(stix); |
| #endif /* RESTORE */ |
| |
| /* let the drive manager shut down its slave thread |
| */ |
| drivep = drivepp[stix]; |
| (*drivep->d_opsp->do_quit)(drivep); |
| |
| return exitcode; |
| } |
| |
| |
| /* ARGSUSED */ |
| static void |
| prompt_prog_cb(void *uctxp, dlog_pcbp_t pcb, void *pctxp) |
| { |
| /* query: ask for a dump label |
| */ |
| (*pcb)(pctxp, |
| progrpt_enabledpr |
| ? |
| _("please enter seconds between progress reports, " |
| "or 0 to disable") |
| : |
| _("please enter seconds between progress reports")); |
| } |
| |
| /* SIGINTR dialog |
| * |
| * side affect is to change verbosity level. |
| * return code of BOOL_TRUE indicates a stop was requested. |
| */ |
| #define PREAMBLEMAX (7 + 2 * STREAM_SIMMAX) |
| #define QUERYMAX 3 |
| #define CHOICEMAX 9 |
| #define ACKMAX 7 |
| #define POSTAMBLEMAX 3 |
| |
| static bool_t |
| sigint_dialog(void) |
| { |
| fold_t fold; |
| char **statline; |
| ix_t i; |
| size_t statlinecnt; |
| char *preamblestr[PREAMBLEMAX]; |
| size_t preamblecnt; |
| char *querystr[QUERYMAX]; |
| size_t querycnt; |
| char *choicestr[CHOICEMAX]; |
| size_t choicecnt; |
| char *ackstr[ACKMAX]; |
| size_t ackcnt; |
| char *postamblestr[POSTAMBLEMAX]; |
| size_t postamblecnt; |
| size_t interruptix; |
| size_t verbosityix; |
| size_t metricsix; |
| size_t controlix; |
| size_t ioix; |
| size_t mediachangeix; |
| #ifdef RESTORE |
| size_t piix; |
| size_t roix; |
| #endif /* RESTORE */ |
| size_t progix; |
| size_t mllevix; |
| size_t mlssix; |
| size_t mltsix; |
| size_t continueix; |
| size_t allix; |
| size_t nochangeix; |
| size_t responseix; |
| int ssselected = 0; |
| bool_t stop_requested = BOOL_FALSE; |
| |
| /* preamble: the content status line, indicate if interrupt happening |
| */ |
| fold_init(fold, _("status and control dialog"), '='); |
| statlinecnt = content_statline(&statline); |
| preamblecnt = 0; |
| preamblestr[preamblecnt++ ] = "\n"; |
| preamblestr[preamblecnt++] = fold; |
| preamblestr[preamblecnt++ ] = "\n"; |
| preamblestr[preamblecnt++ ] = "\n"; |
| for (i = 0; i < statlinecnt; i++) { |
| preamblestr[preamblecnt++] = statline[i]; |
| } |
| if (stop_in_progress) { |
| preamblestr[preamblecnt++] = |
| _("\nsession interrupt in progress\n"); |
| } |
| preamblestr[preamblecnt++ ] = "\n"; |
| assert(preamblecnt <= PREAMBLEMAX); |
| dlog_begin(preamblestr, preamblecnt); |
| |
| /* top-level query: a function of session interrupt status |
| */ |
| querycnt = 0; |
| querystr[querycnt++ ] = _("please select one of " |
| "the following operations\n"); |
| assert(querycnt <= QUERYMAX); |
| choicecnt = 0; |
| if (!stop_in_progress) { |
| interruptix = choicecnt; |
| choicestr[choicecnt++ ] = _("interrupt this session"); |
| } else { |
| interruptix = SIZEMAX; /* never happen */ |
| } |
| |
| verbosityix = choicecnt; |
| choicestr[choicecnt++ ] = _("change verbosity"); |
| metricsix = choicecnt; |
| choicestr[choicecnt++ ] = _("display metrics"); |
| if (content_media_change_needed) { |
| mediachangeix = choicecnt; |
| choicestr[choicecnt++ ] = _("confirm media change"); |
| } else { |
| mediachangeix = SIZEMAX; /* never happen */ |
| } |
| controlix = choicecnt; |
| choicestr[choicecnt++ ] = _("other controls"); |
| continueix = choicecnt; |
| choicestr[choicecnt++ ] = _("continue"); |
| assert(choicecnt <= CHOICEMAX); |
| |
| responseix = dlog_multi_query(querystr, |
| querycnt, |
| choicestr, |
| choicecnt, |
| 0, /* hilitestr */ |
| IXMAX, /* hiliteix */ |
| 0, /* defaultstr */ |
| continueix, /* defaultix */ |
| DLOG_TIMEOUT, /* timeout */ |
| continueix, /* timeout ix */ |
| continueix, /* sigint ix */ |
| continueix, /* sighup ix */ |
| continueix); /* sigquit ix */ |
| if (responseix == interruptix) { |
| ackcnt = 0; |
| ackstr[ackcnt++ ] = "\n"; |
| dlog_multi_ack(ackstr, |
| ackcnt); |
| querycnt = 0; |
| querystr[querycnt++ ] = _("please confirm\n"); |
| assert(querycnt <= QUERYMAX); |
| choicecnt = 0; |
| interruptix = choicecnt; |
| choicestr[choicecnt++ ] = _("interrupt this session"); |
| nochangeix = choicecnt; |
| choicestr[choicecnt++ ] = _("continue"); |
| assert(choicecnt <= CHOICEMAX); |
| responseix = dlog_multi_query(querystr, |
| querycnt, |
| choicestr, |
| choicecnt, |
| 0, /* hilitestr */ |
| IXMAX, /* hiliteix */ |
| 0, /* defaultstr */ |
| nochangeix, /* defaultix */ |
| DLOG_TIMEOUT,/* timeout */ |
| nochangeix, /* timeout ix */ |
| nochangeix, /* sigint ix */ |
| nochangeix, /* sighup ix */ |
| nochangeix);/* sigquit ix */ |
| ackcnt = 0; |
| if (responseix == nochangeix) { |
| ackstr[ackcnt++ ] = _("continuing\n"); |
| } else { |
| ackstr[ackcnt++ ] = _("interrupt request accepted\n"); |
| stop_requested = BOOL_TRUE; |
| } |
| dlog_multi_ack(ackstr, |
| ackcnt); |
| } else if (responseix == verbosityix) { |
| ackcnt = 0; |
| ackstr[ackcnt++ ] = "\n"; |
| dlog_multi_ack(ackstr, |
| ackcnt); |
| querycnt = 0; |
| querystr[querycnt++ ] = _("please select one of " |
| "the following subsystems\n"); |
| assert(querycnt <= QUERYMAX); |
| choicecnt = 0; |
| /* number of lines must match number of subsystems |
| */ |
| for (choicecnt = 0; choicecnt < MLOG_SS_CNT; choicecnt++) { |
| choicestr[choicecnt] = mlog_ss_names[choicecnt]; |
| } |
| allix = choicecnt; |
| choicestr[choicecnt++ ] = _("all of the above"); |
| nochangeix = choicecnt; |
| choicestr[choicecnt++ ] = _("no change"); |
| assert(choicecnt <= CHOICEMAX); |
| responseix = dlog_multi_query(querystr, |
| querycnt, |
| choicestr, |
| choicecnt, |
| 0, /* hilitestr */ |
| IXMAX, /* hiliteix */ |
| 0, /* defaultstr */ |
| allix, /* defaultix */ |
| DLOG_TIMEOUT,/* timeout */ |
| nochangeix, /* timeout ix */ |
| nochangeix, /* sigint ix */ |
| nochangeix, /* sighup ix */ |
| nochangeix);/* sigquit ix */ |
| ackcnt = 0; |
| if (responseix == nochangeix) { |
| ackstr[ackcnt++ ] = _("no change\n"); |
| } else if (responseix == allix) { |
| ssselected = -1; |
| ackstr[ackcnt++ ] = _("all subsystems selected\n\n"); |
| } else { |
| ssselected = (int)responseix; |
| ackstr[ackcnt++ ] = "\n"; |
| } |
| dlog_multi_ack(ackstr, |
| ackcnt); |
| if (responseix != nochangeix) { |
| querycnt = 0; |
| querystr[querycnt++ ] = ("please select one of the " |
| "following verbosity levels\n"); |
| assert(querycnt <= QUERYMAX); |
| choicecnt = 0; |
| choicestr[choicecnt++ ] = _("silent"); |
| choicestr[choicecnt++ ] = _("verbose"); |
| choicestr[choicecnt++ ] = _("trace"); |
| choicestr[choicecnt++ ] = _("debug"); |
| choicestr[choicecnt++ ] = _("nitty"); |
| choicestr[choicecnt++ ] = _("nitty + 1"); |
| nochangeix = choicecnt; |
| choicestr[choicecnt++ ] = _("no change"); |
| assert(choicecnt <= CHOICEMAX); |
| responseix = dlog_multi_query(querystr, |
| querycnt, |
| choicestr, |
| choicecnt, |
| ssselected == -1 |
| ? |
| 0 |
| : |
| _(" (current)"),/* hilitestr */ |
| ssselected == -1 |
| ? |
| IXMAX |
| : |
| (ix_t)mlog_level_ss[ssselected], /* hiliteix */ |
| 0, /* defaultstr */ |
| nochangeix,/* defaultix */ |
| DLOG_TIMEOUT,/* timeout */ |
| nochangeix, /* timeout ix */ |
| nochangeix, /* sigint ix */ |
| nochangeix, /* sighup ix */ |
| nochangeix);/* sigquit ix */ |
| ackcnt = 0; |
| if (responseix == nochangeix |
| || |
| (ssselected >= 0 |
| && |
| responseix |
| == |
| (ix_t)mlog_level_ss[ssselected])) { |
| ackstr[ackcnt++ ] = _("no change\n"); |
| } else { |
| if (ssselected < 0) { |
| ix_t ssix; |
| assert(ssselected == -1); |
| for (ssix = 0 |
| ; |
| ssix < MLOG_SS_CNT |
| ; |
| ssix++) { |
| mlog_level_ss[ssix] = |
| (int)responseix; |
| } |
| } else { |
| mlog_level_ss[ssselected] = |
| (int)responseix; |
| } |
| ackstr[ackcnt++ ] = _("level changed\n"); |
| } |
| dlog_multi_ack(ackstr, |
| ackcnt); |
| } |
| } else if (responseix == metricsix) { |
| ackcnt = 0; |
| ackstr[ackcnt++ ] = "\n"; |
| dlog_multi_ack(ackstr, |
| ackcnt); |
| querycnt = 0; |
| querystr[querycnt++ ] = _("please select one of " |
| "the following metrics\n"); |
| assert(querycnt <= QUERYMAX); |
| choicecnt = 0; |
| ioix = choicecnt; |
| choicestr[choicecnt++ ] = _("I/O"); |
| #ifdef RESTORE |
| piix = choicecnt; |
| choicestr[choicecnt++ ] = _("media inventory status"); |
| roix = choicecnt; |
| choicestr[choicecnt++ ] = _("needed media objects"); |
| #endif /* RESTORE */ |
| nochangeix = choicecnt; |
| choicestr[choicecnt++ ] = _("continue"); |
| assert(choicecnt <= CHOICEMAX); |
| responseix = dlog_multi_query(querystr, |
| querycnt, |
| choicestr, |
| choicecnt, |
| 0, /* hilitestr */ |
| IXMAX, /* hiliteix */ |
| 0, /* defaultstr */ |
| nochangeix, /* defaultix */ |
| DLOG_TIMEOUT, |
| nochangeix, /* timeout ix */ |
| nochangeix, /* sigint ix */ |
| nochangeix, /* sighup ix */ |
| nochangeix);/* sigquit ix */ |
| if (responseix != nochangeix) { |
| ackcnt = 0; |
| ackstr[ackcnt++ ] = "\n"; |
| dlog_multi_ack(ackstr, |
| ackcnt); |
| } |
| if (responseix == ioix) { |
| drive_display_metrics(); |
| #ifdef RESTORE |
| } else if (responseix == piix) { |
| content_showinv(); |
| } else if (responseix == roix) { |
| content_showremainingobjects(); |
| #endif /* RESTORE */ |
| } |
| |
| if (responseix != nochangeix) { |
| querycnt = 0; |
| querystr[querycnt++ ] = "\n"; |
| assert(querycnt <= QUERYMAX); |
| choicecnt = 0; |
| nochangeix = choicecnt; |
| choicestr[choicecnt++ ] = _("continue"); |
| assert(choicecnt <= CHOICEMAX); |
| responseix = dlog_multi_query(querystr, |
| querycnt, |
| choicestr, |
| choicecnt, |
| 0, /* hilitestr */ |
| IXMAX, /* hiliteix */ |
| 0, /* defaultstr */ |
| nochangeix,/* defaultix*/ |
| DLOG_TIMEOUT, |
| nochangeix,/*timeout ix*/ |
| nochangeix,/* sigint ix*/ |
| nochangeix,/* sighup ix*/ |
| nochangeix);/*sigquitix*/ |
| } |
| ackcnt = 0; |
| ackstr[ackcnt++ ] = _("continuing\n"); |
| dlog_multi_ack(ackstr, |
| ackcnt); |
| } else if (responseix == mediachangeix) { |
| ackcnt = 0; |
| dlog_multi_ack(ackstr, |
| ackcnt); |
| ackcnt = 0; |
| ackstr[ackcnt++] = content_mediachange_query(); |
| dlog_multi_ack(ackstr, |
| ackcnt); |
| } else if (responseix == controlix) { |
| ackcnt = 0; |
| ackstr[ackcnt++ ] = "\n"; |
| dlog_multi_ack(ackstr, |
| ackcnt); |
| querycnt = 0; |
| querystr[querycnt++ ] = _("please select one of " |
| "the following controls\n"); |
| assert(querycnt <= QUERYMAX); |
| choicecnt = 0; |
| progix = choicecnt; |
| if (progrpt_enabledpr) { |
| choicestr[choicecnt++ ] = _("change interval of " |
| "or disable progress reports"); |
| } else { |
| choicestr[choicecnt++ ] = _("enable progress reports"); |
| } |
| mllevix = choicecnt; |
| if (mlog_showlevel) { |
| choicestr[choicecnt++ ] = _("hide log message levels"); |
| } else { |
| choicestr[choicecnt++ ] = _("show log message levels"); |
| } |
| mlssix = choicecnt; |
| if (mlog_showss) { |
| choicestr[choicecnt++ ] = _("hide log message subsystems"); |
| } else { |
| choicestr[choicecnt++ ] = _("show log message subsystems"); |
| } |
| mltsix = choicecnt; |
| if (mlog_timestamp) { |
| choicestr[choicecnt++ ] = _("hide log message timestamps"); |
| } else { |
| choicestr[choicecnt++ ] = _("show log message timestamps"); |
| } |
| nochangeix = choicecnt; |
| choicestr[choicecnt++ ] = _("continue"); |
| assert(choicecnt <= CHOICEMAX); |
| responseix = dlog_multi_query(querystr, |
| querycnt, |
| choicestr, |
| choicecnt, |
| 0, /* hilitestr */ |
| IXMAX, /* hiliteix */ |
| 0, /* defaultstr */ |
| nochangeix, /* defaultix */ |
| DLOG_TIMEOUT, |
| nochangeix, /* timeout ix */ |
| nochangeix, /* sigint ix */ |
| nochangeix, /* sighup ix */ |
| nochangeix);/* sigquit ix */ |
| ackcnt = 0; |
| if (responseix == progix) { |
| char buf[10]; |
| const size_t ncix = 1; |
| const size_t okix = 2; |
| |
| ackstr[ackcnt++ ] = "\n"; |
| dlog_multi_ack(ackstr, |
| ackcnt); |
| ackcnt = 0; |
| responseix = dlog_string_query(prompt_prog_cb, |
| 0, |
| buf, |
| sizeof(buf), |
| DLOG_TIMEOUT, |
| ncix,/* timeout ix */ |
| ncix, /* sigint ix */ |
| ncix, /* sighup ix */ |
| ncix, /* sigquit ix */ |
| okix); |
| if (responseix == okix) { |
| int newinterval; |
| newinterval = atoi(buf); |
| if (!strlen(buf)) { |
| ackstr[ackcnt++ ] = _("no change\n"); |
| } else if (newinterval > 0) { |
| time32_t newdeadline; |
| char intervalbuf[64]; |
| newdeadline = time(0) + (time32_t)newinterval; |
| if (progrpt_enabledpr) { |
| if ((time32_t)newinterval == progrpt_interval) { |
| ackstr[ackcnt++ ] = _("no change\n"); |
| } else { |
| ackstr[ackcnt++ ] = _("changing progress report interval to "); |
| sprintf(intervalbuf, |
| _("%d seconds\n"), |
| newinterval); |
| assert(strlen(intervalbuf) |
| < |
| sizeof(intervalbuf)); |
| ackstr[ackcnt++] = intervalbuf; |
| if (progrpt_deadline > newdeadline) { |
| progrpt_deadline = newdeadline; |
| } |
| } |
| } else { |
| ackstr[ackcnt++ ] = _("enabling progress reports at "); |
| sprintf(intervalbuf, |
| _("%d second intervals\n"), |
| newinterval); |
| assert(strlen(intervalbuf) |
| < |
| sizeof(intervalbuf)); |
| ackstr[ackcnt++] = intervalbuf; |
| progrpt_enabledpr = BOOL_TRUE; |
| progrpt_deadline = newdeadline; |
| } |
| progrpt_interval = (time32_t)newinterval; |
| } else { |
| if (progrpt_enabledpr) { |
| ackstr[ackcnt++ ] = _("disabling progress reports\n"); |
| } else { |
| ackstr[ackcnt++ ] = _("no change\n"); |
| } |
| progrpt_enabledpr = BOOL_FALSE; |
| } |
| } else { |
| ackstr[ackcnt++ ] = _("no change\n"); |
| } |
| } else if (responseix == mllevix) { |
| mlog_showlevel = !mlog_showlevel; |
| if (mlog_showlevel) { |
| ackstr[ackcnt++ ] = _("showing log message levels\n"); |
| } else { |
| ackstr[ackcnt++ ] = _("hiding log message levels\n"); |
| } |
| } else if (responseix == mlssix) { |
| mlog_showss = !mlog_showss; |
| if (mlog_showss) { |
| ackstr[ackcnt++ ] = _("showing log message subsystems\n"); |
| } else { |
| ackstr[ackcnt++ ] = _("hiding log message subsystems\n"); |
| } |
| } else if (responseix == mltsix) { |
| mlog_timestamp = !mlog_timestamp; |
| if (mlog_timestamp) { |
| ackstr[ackcnt++ ] = _("showing log message timestamps\n"); |
| } else { |
| ackstr[ackcnt++ ] = _("hiding log message timestamps\n"); |
| } |
| } |
| dlog_multi_ack(ackstr, |
| ackcnt); |
| } else { |
| ackcnt = 0; |
| ackstr[ackcnt++ ] = _("continuing\n"); |
| dlog_multi_ack(ackstr, |
| ackcnt); |
| } |
| |
| fold_init(fold, _("end dialog"), '-'); |
| postamblecnt = 0; |
| postamblestr[postamblecnt++ ] = "\n"; |
| postamblestr[postamblecnt++] = fold; |
| postamblestr[postamblecnt++ ] = "\n\n"; |
| assert(postamblecnt <= POSTAMBLEMAX); |
| dlog_end(postamblestr, |
| postamblecnt); |
| |
| return stop_requested; |
| } |
| |
| static char * |
| sigintstr(void) |
| { |
| int ttyfd; |
| static char buf[20]; |
| struct termios termios; |
| cc_t intchr; |
| int rval; |
| |
| ttyfd = dlog_fd(); |
| if (ttyfd == -1) { |
| return 0; |
| } |
| |
| rval = tcgetattr(ttyfd, &termios); |
| if (rval) { |
| mlog(MLOG_NITTY | MLOG_PROC, |
| "could not get controlling terminal information: %s\n", |
| strerror(errno)); |
| return 0; |
| } |
| |
| intchr = termios.c_cc[VINTR]; |
| mlog(MLOG_NITTY | MLOG_PROC, |
| "tty fd: %d; terminal interrupt character: %c (0%o)\n", |
| ttyfd, |
| intchr, |
| intchr); |
| |
| if (intchr < ' ') { |
| sprintf(buf, "^%c", intchr + '@'); |
| } else if (intchr == 0177) { |
| sprintf(buf, "DEL"); |
| } else { |
| sprintf(buf, "%c", intchr); |
| } |
| assert(strlen(buf) < sizeof(buf)); |
| |
| return buf; |
| } |
| |
| #ifdef DUMP |
| static bool_t |
| set_rlimits(void) |
| #endif /* DUMP */ |
| #ifdef RESTORE |
| static bool_t |
| set_rlimits(size64_t *vmszp) |
| #endif /* RESTORE */ |
| { |
| struct rlimit64 rlimit64; |
| #ifdef RESTORE |
| size64_t vmsz; |
| #endif /* RESTORE */ |
| /* REFERENCED */ |
| int rval; |
| |
| assert(minstacksz <= maxstacksz); |
| |
| rval = getrlimit64(RLIMIT_AS, &rlimit64); |
| |
| assert(!rval); |
| mlog(MLOG_NITTY | MLOG_NOLOCK | MLOG_PROC, |
| "RLIMIT_AS org cur 0x%llx max 0x%llx\n", |
| rlimit64.rlim_cur, |
| rlimit64.rlim_max); |
| #ifdef RESTORE |
| if (rlimit64.rlim_cur != RLIM64_INFINITY) { |
| rlimit64.rlim_cur = rlimit64.rlim_max; |
| (void)setrlimit64(RLIMIT_AS, &rlimit64); |
| rval = getrlimit64(RLIMIT_AS, &rlimit64); |
| assert(!rval); |
| mlog(MLOG_NITTY | MLOG_NOLOCK | MLOG_PROC, |
| "RLIMIT_VMEM now cur 0x%llx max 0x%llx\n", |
| rlimit64.rlim_cur, |
| rlimit64.rlim_max); |
| } |
| |
| vmsz = (size64_t)rlimit64.rlim_cur; |
| #endif /* RESTORE */ |
| |
| assert(minstacksz <= maxstacksz); |
| rval = getrlimit64(RLIMIT_STACK, &rlimit64); |
| assert(!rval); |
| mlog(MLOG_NITTY | MLOG_NOLOCK | MLOG_PROC, |
| "RLIMIT_STACK org cur 0x%llx max 0x%llx\n", |
| rlimit64.rlim_cur, |
| rlimit64.rlim_max); |
| if (rlimit64.rlim_cur < minstacksz) { |
| if (rlimit64.rlim_max < minstacksz) { |
| mlog(MLOG_DEBUG |
| | |
| MLOG_NOLOCK |
| | |
| MLOG_PROC, |
| "raising stack size hard limit " |
| "from 0x%llx to 0x%llx\n", |
| rlimit64.rlim_max, |
| minstacksz); |
| rlimit64.rlim_cur = minstacksz; |
| rlimit64.rlim_max = minstacksz; |
| (void)setrlimit64(RLIMIT_STACK, &rlimit64); |
| rval = getrlimit64(RLIMIT_STACK, &rlimit64); |
| assert(!rval); |
| if (rlimit64.rlim_cur < minstacksz) { |
| mlog(MLOG_NORMAL |
| | |
| MLOG_WARNING |
| | |
| MLOG_NOLOCK |
| | |
| MLOG_PROC, |
| _("unable to raise stack size hard limit " |
| "from 0x%llx to 0x%llx\n"), |
| rlimit64.rlim_max, |
| minstacksz); |
| } |
| } else { |
| mlog(MLOG_DEBUG |
| | |
| MLOG_NOLOCK |
| | |
| MLOG_PROC, |
| "raising stack size soft limit " |
| "from 0x%llx to 0x%llx\n", |
| rlimit64.rlim_cur, |
| minstacksz); |
| rlimit64.rlim_cur = minstacksz; |
| (void)setrlimit64(RLIMIT_STACK, &rlimit64); |
| rval = getrlimit64(RLIMIT_STACK, &rlimit64); |
| assert(!rval); |
| if (rlimit64.rlim_cur < minstacksz) { |
| mlog(MLOG_NORMAL |
| | |
| MLOG_WARNING |
| | |
| MLOG_NOLOCK |
| | |
| MLOG_PROC, |
| _("unable to raise stack size soft limit " |
| "from 0x%llx to 0x%llx\n"), |
| rlimit64.rlim_cur, |
| minstacksz); |
| } |
| } |
| } else if (rlimit64.rlim_cur > maxstacksz) { |
| mlog(MLOG_DEBUG |
| | |
| MLOG_NOLOCK |
| | |
| MLOG_PROC, |
| "lowering stack size soft limit " |
| "from 0x%llx to 0x%llx\n", |
| rlimit64.rlim_cur, |
| maxstacksz); |
| rlimit64.rlim_cur = maxstacksz; |
| (void)setrlimit64(RLIMIT_STACK, &rlimit64); |
| rval = getrlimit64(RLIMIT_STACK, &rlimit64); |
| assert(!rval); |
| if (rlimit64.rlim_cur > maxstacksz) { |
| mlog(MLOG_NORMAL |
| | |
| MLOG_WARNING |
| | |
| MLOG_NOLOCK |
| | |
| MLOG_PROC, |
| _("unable to lower stack size soft limit " |
| "from 0x%llx to 0x%llx\n"), |
| rlimit64.rlim_cur, |
| maxstacksz); |
| } |
| } |
| mlog(MLOG_NITTY | MLOG_NOLOCK | MLOG_PROC, |
| "RLIMIT_STACK new cur 0x%llx max 0x%llx\n", |
| rlimit64.rlim_cur, |
| rlimit64.rlim_max); |
| |
| rval = getrlimit64(RLIMIT_DATA, &rlimit64); |
| assert(!rval); |
| mlog(MLOG_NITTY | MLOG_NOLOCK | MLOG_PROC, |
| "RLIMIT_DATA org cur 0x%llx max 0x%llx\n", |
| rlimit64.rlim_cur, |
| rlimit64.rlim_max); |
| |
| rval = getrlimit64(RLIMIT_FSIZE, &rlimit64); |
| assert(!rval); |
| mlog(MLOG_NITTY | MLOG_NOLOCK | MLOG_PROC, |
| "RLIMIT_FSIZE org cur 0x%llx max 0x%llx\n", |
| rlimit64.rlim_cur, |
| rlimit64.rlim_max); |
| rlimit64.rlim_cur = rlimit64.rlim_max; |
| (void)setrlimit64(RLIMIT_FSIZE, &rlimit64); |
| rlimit64.rlim_cur = RLIM64_INFINITY; |
| (void)setrlimit64(RLIMIT_FSIZE, &rlimit64); |
| rval = getrlimit64(RLIMIT_FSIZE, &rlimit64); |
| assert(!rval); |
| mlog(MLOG_NITTY | MLOG_NOLOCK | MLOG_PROC, |
| "RLIMIT_FSIZE now cur 0x%llx max 0x%llx\n", |
| rlimit64.rlim_cur, |
| rlimit64.rlim_max); |
| |
| rval = getrlimit64(RLIMIT_CPU, &rlimit64); |
| assert(!rval); |
| mlog(MLOG_NITTY | MLOG_NOLOCK | MLOG_PROC, |
| "RLIMIT_CPU cur 0x%llx max 0x%llx\n", |
| rlimit64.rlim_cur, |
| rlimit64.rlim_max); |
| rlimit64.rlim_cur = rlimit64.rlim_max; |
| (void)setrlimit64(RLIMIT_CPU, &rlimit64); |
| rval = getrlimit64(RLIMIT_CPU, &rlimit64); |
| assert(!rval); |
| mlog(MLOG_NITTY | MLOG_NOLOCK | MLOG_PROC, |
| "RLIMIT_CPU now cur 0x%llx max 0x%llx\n", |
| rlimit64.rlim_cur, |
| rlimit64.rlim_max); |
| |
| #ifdef RESTORE |
| *vmszp = vmsz; |
| #endif /* RESTORE */ |
| return BOOL_TRUE; |
| } |
| |
| struct sig_printmap { |
| int num; |
| char *string; |
| }; |
| |
| typedef struct sig_printmap sig_printmap_t; |
| |
| static sig_printmap_t sig_printmap[] = { |
| {SIGHUP, "SIGHUP"}, |
| {SIGINT, "SIGINT"}, |
| {SIGQUIT, "SIGQUIT"}, |
| {SIGILL, "SIGILL"}, |
| {SIGABRT, "SIGABRT"}, |
| {SIGFPE, "SIGFPE"}, |
| {SIGBUS, "SIGBUS"}, |
| {SIGSEGV, "SIGSEGV"}, |
| #ifdef SIGSYS |
| {SIGSYS, "SIGSYS"}, |
| #endif |
| {SIGPIPE, "SIGPIPE"}, |
| {SIGALRM, "SIGALRM"}, |
| {SIGTERM, "SIGTERM"}, |
| {SIGUSR1, "SIGUSR1"}, |
| {SIGUSR2, "SIGUSR2"}, |
| {SIGCHLD, "SIGCHLD"}, |
| {SIGPWR, "SIGPWR"}, |
| {SIGURG, "SIGURG"}, |
| {SIGPOLL, "SIGPOLL"}, |
| {SIGXCPU, "SIGXCPU"}, |
| {SIGXFSZ, "SIGXFSZ"}, |
| #if HIDDEN |
| {SIGRTMIN, "SIGRTMIN"}, |
| {SIGRTMAX, "SIGRTMAX"}, |
| #endif |
| {0, "???"} |
| }; |
| |
| static char * |
| sig_numstring(int num) |
| { |
| sig_printmap_t *p = sig_printmap; |
| sig_printmap_t *endp = sig_printmap |
| + |
| (sizeof(sig_printmap) |
| / |
| sizeof(sig_printmap[0])); |
| for (; p < endp; p++) { |
| if (p->num == num) { |
| return p->string; |
| } |
| } |
| |
| return "???"; |
| } |
| |
| static char * |
| strpbrkquotes(char *p, const char *sep) |
| { |
| bool_t prevcharwasbackslash = BOOL_FALSE; |
| bool_t inquotes = BOOL_FALSE; |
| |
| for (;; p++) { |
| if (*p == 0) { |
| return 0; |
| } |
| |
| if (*p == '\\') { |
| if (!prevcharwasbackslash) { |
| prevcharwasbackslash = BOOL_TRUE; |
| } else { |
| prevcharwasbackslash = BOOL_FALSE; |
| } |
| continue; |
| } |
| |
| if (*p == '"') { |
| if (prevcharwasbackslash) { |
| prevcharwasbackslash = BOOL_FALSE; |
| continue; |
| } |
| if (inquotes) { |
| inquotes = BOOL_FALSE; |
| } else { |
| inquotes = BOOL_TRUE; |
| } |
| continue; |
| } |
| |
| if (!inquotes) { |
| if (strchr(sep, (int)(*p))) { |
| return p; |
| } |
| } |
| |
| prevcharwasbackslash = BOOL_FALSE; |
| } |
| /* NOTREACHED */ |
| } |
| |
| static char * |
| stripquotes(char *p) |
| { |
| size_t len = strlen(p); |
| char *endp; |
| char *nextp; |
| bool_t justremovedbackslash; |
| |
| if (len > 2 && p[0 ] == '"') { |
| p++; |
| len--; |
| if (len && p[len - 1 ] == '"') { |
| p[len - 1] = 0; |
| len--; |
| } |
| } |
| |
| endp = p + len; |
| justremovedbackslash = BOOL_FALSE; |
| |
| for (nextp = p; nextp < endp;) { |
| if (*nextp == '\\' && !justremovedbackslash) { |
| shiftleftby1(nextp, endp); |
| endp--; |
| justremovedbackslash = BOOL_TRUE; |
| } else { |
| justremovedbackslash = BOOL_FALSE; |
| nextp++; |
| } |
| } |
| |
| return p; |
| } |
| |
| static void |
| shiftleftby1(char *p, char *endp) |
| { |
| for (; p < endp; p++) { |
| *p = p[1]; |
| } |
| } |