static void test_parse_nice(void) { int n; assert_se(parse_nice("0", &n) >= 0 && n == 0); assert_se(parse_nice("+0", &n) >= 0 && n == 0); assert_se(parse_nice("-1", &n) >= 0 && n == -1); assert_se(parse_nice("-2", &n) >= 0 && n == -2); assert_se(parse_nice("1", &n) >= 0 && n == 1); assert_se(parse_nice("2", &n) >= 0 && n == 2); assert_se(parse_nice("+1", &n) >= 0 && n == 1); assert_se(parse_nice("+2", &n) >= 0 && n == 2); assert_se(parse_nice("-20", &n) >= 0 && n == -20); assert_se(parse_nice("19", &n) >= 0 && n == 19); assert_se(parse_nice("+19", &n) >= 0 && n == 19); assert_se(parse_nice("", &n) == -EINVAL); assert_se(parse_nice("-", &n) == -EINVAL); assert_se(parse_nice("+", &n) == -EINVAL); assert_se(parse_nice("xx", &n) == -EINVAL); assert_se(parse_nice("-50", &n) == -ERANGE); assert_se(parse_nice("50", &n) == -ERANGE); assert_se(parse_nice("+50", &n) == -ERANGE); assert_se(parse_nice("-21", &n) == -ERANGE); assert_se(parse_nice("20", &n) == -ERANGE); assert_se(parse_nice("+20", &n) == -ERANGE); }
static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, ARG_USER, ARG_SYSTEM, ARG_SCOPE, ARG_UNIT, ARG_DESCRIPTION, ARG_SLICE, ARG_SEND_SIGHUP, ARG_SERVICE_TYPE, ARG_EXEC_USER, ARG_EXEC_GROUP, ARG_NICE, ARG_ON_ACTIVE, ARG_ON_BOOT, ARG_ON_STARTUP, ARG_ON_UNIT_ACTIVE, ARG_ON_UNIT_INACTIVE, ARG_ON_CALENDAR, ARG_TIMER_PROPERTY, ARG_NO_BLOCK, ARG_NO_ASK_PASSWORD, ARG_WAIT, }; static const struct option options[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, ARG_VERSION }, { "user", no_argument, NULL, ARG_USER }, { "system", no_argument, NULL, ARG_SYSTEM }, { "scope", no_argument, NULL, ARG_SCOPE }, { "unit", required_argument, NULL, ARG_UNIT }, { "description", required_argument, NULL, ARG_DESCRIPTION }, { "slice", required_argument, NULL, ARG_SLICE }, { "remain-after-exit", no_argument, NULL, 'r' }, { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP }, { "host", required_argument, NULL, 'H' }, { "machine", required_argument, NULL, 'M' }, { "service-type", required_argument, NULL, ARG_SERVICE_TYPE }, { "wait", no_argument, NULL, ARG_WAIT }, { "uid", required_argument, NULL, ARG_EXEC_USER }, { "gid", required_argument, NULL, ARG_EXEC_GROUP }, { "nice", required_argument, NULL, ARG_NICE }, { "setenv", required_argument, NULL, 'E' }, { "property", required_argument, NULL, 'p' }, { "tty", no_argument, NULL, 't' }, /* deprecated */ { "pty", no_argument, NULL, 't' }, { "quiet", no_argument, NULL, 'q' }, { "on-active", required_argument, NULL, ARG_ON_ACTIVE }, { "on-boot", required_argument, NULL, ARG_ON_BOOT }, { "on-startup", required_argument, NULL, ARG_ON_STARTUP }, { "on-unit-active", required_argument, NULL, ARG_ON_UNIT_ACTIVE }, { "on-unit-inactive", required_argument, NULL, ARG_ON_UNIT_INACTIVE }, { "on-calendar", required_argument, NULL, ARG_ON_CALENDAR }, { "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY }, { "no-block", no_argument, NULL, ARG_NO_BLOCK }, { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, {}, }; int r, c; assert(argc >= 0); assert(argv); while ((c = getopt_long(argc, argv, "+hrH:M:E:p:tq", options, NULL)) >= 0) switch (c) { case 'h': help(); return 0; case ARG_VERSION: return version(); case ARG_NO_ASK_PASSWORD: arg_ask_password = false; break; case ARG_USER: arg_user = true; break; case ARG_SYSTEM: arg_user = false; break; case ARG_SCOPE: arg_scope = true; break; case ARG_UNIT: arg_unit = optarg; break; case ARG_DESCRIPTION: arg_description = optarg; break; case ARG_SLICE: arg_slice = optarg; break; case ARG_SEND_SIGHUP: arg_send_sighup = true; break; case 'r': arg_remain_after_exit = true; break; case 'H': arg_transport = BUS_TRANSPORT_REMOTE; arg_host = optarg; break; case 'M': arg_transport = BUS_TRANSPORT_MACHINE; arg_host = optarg; break; case ARG_SERVICE_TYPE: arg_service_type = optarg; break; case ARG_EXEC_USER: arg_exec_user = optarg; break; case ARG_EXEC_GROUP: arg_exec_group = optarg; break; case ARG_NICE: r = parse_nice(optarg, &arg_nice); if (r < 0) return log_error_errno(r, "Failed to parse nice value: %s", optarg); arg_nice_set = true; break; case 'E': if (strv_extend(&arg_environment, optarg) < 0) return log_oom(); break; case 'p': if (strv_extend(&arg_property, optarg) < 0) return log_oom(); break; case 't': arg_pty = true; break; case 'q': arg_quiet = true; break; case ARG_ON_ACTIVE: r = parse_sec(optarg, &arg_on_active); if (r < 0) { log_error("Failed to parse timer value: %s", optarg); return r; } break; case ARG_ON_BOOT: r = parse_sec(optarg, &arg_on_boot); if (r < 0) { log_error("Failed to parse timer value: %s", optarg); return r; } break; case ARG_ON_STARTUP: r = parse_sec(optarg, &arg_on_startup); if (r < 0) { log_error("Failed to parse timer value: %s", optarg); return r; } break; case ARG_ON_UNIT_ACTIVE: r = parse_sec(optarg, &arg_on_unit_active); if (r < 0) { log_error("Failed to parse timer value: %s", optarg); return r; } break; case ARG_ON_UNIT_INACTIVE: r = parse_sec(optarg, &arg_on_unit_inactive); if (r < 0) { log_error("Failed to parse timer value: %s", optarg); return r; } break; case ARG_ON_CALENDAR: { CalendarSpec *spec = NULL; r = calendar_spec_from_string(optarg, &spec); if (r < 0) { log_error("Invalid calendar spec: %s", optarg); return r; } calendar_spec_free(spec); arg_on_calendar = optarg; break; } case ARG_TIMER_PROPERTY: if (strv_extend(&arg_timer_property, optarg) < 0) return log_oom(); break; case ARG_NO_BLOCK: arg_no_block = true; break; case ARG_WAIT: arg_wait = true; break; case '?': return -EINVAL; default: assert_not_reached("Unhandled option"); } if ((optind >= argc) && (!arg_unit || !with_timer())) { log_error("Command line to execute required."); return -EINVAL; } if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) { log_error("Execution in user context is not supported on non-local systems."); return -EINVAL; } if (arg_scope && arg_transport != BUS_TRANSPORT_LOCAL) { log_error("Scope execution is not supported on non-local systems."); return -EINVAL; } if (arg_scope && (arg_remain_after_exit || arg_service_type)) { log_error("--remain-after-exit and --service-type= are not supported in --scope mode."); return -EINVAL; } if (arg_pty && (with_timer() || arg_scope)) { log_error("--pty is not compatible in timer or --scope mode."); return -EINVAL; } if (arg_pty && arg_transport == BUS_TRANSPORT_REMOTE) { log_error("--pty is only supported when connecting to the local system or containers."); return -EINVAL; } if (arg_scope && with_timer()) { log_error("Timer options are not supported in --scope mode."); return -EINVAL; } if (arg_timer_property && !with_timer()) { log_error("--timer-property= has no effect without any other timer options."); return -EINVAL; } if (arg_wait) { if (arg_no_block) { log_error("--wait may not be combined with --no-block."); return -EINVAL; } if (with_timer()) { log_error("--wait may not be combined with timer operations."); return -EINVAL; } if (arg_scope) { log_error("--wait may not be combined with --scope."); return -EINVAL; } } return 1; }