static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, ARG_NO_ASK_PASSWORD, ARG_USER, ARG_SYSTEM, ARG_SCOPE, ARG_UNIT, ARG_DESCRIPTION, ARG_SLICE, ARG_SEND_SIGHUP, ARG_EXEC_USER, ARG_EXEC_GROUP, ARG_SERVICE_TYPE, ARG_NICE, ARG_SETENV, ARG_TTY, 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, }; 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 }, { "uid", required_argument, NULL, ARG_EXEC_USER }, { "gid", required_argument, NULL, ARG_EXEC_GROUP }, { "nice", required_argument, NULL, ARG_NICE }, { "setenv", required_argument, NULL, ARG_SETENV }, { "property", required_argument, NULL, 'p' }, { "tty", 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:p:tq", options, NULL)) >= 0) switch (c) { case 'h': help(); return 0; case ARG_NO_ASK_PASSWORD: arg_ask_password = false; break; case ARG_VERSION: return version(); 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 = safe_atoi(optarg, &arg_nice); if (r < 0 || arg_nice < PRIO_MIN || arg_nice >= PRIO_MAX) { log_error("Failed to parse nice value"); return -EINVAL; } arg_nice_set = true; break; case ARG_SETENV: 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 '?': 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; } return 1; }
int main(int argc, char* argv[]) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_free_ char *description = NULL, *command = NULL; int r, retval = EXIT_SUCCESS; log_parse_environment(); log_open(); r = parse_argv(argc, argv); if (r <= 0) goto finish; if (argc > optind && arg_transport == BUS_TRANSPORT_LOCAL) { /* Patch in an absolute path */ r = find_binary(argv[optind], &command); if (r < 0) { log_error_errno(r, "Failed to find executable %s: %m", argv[optind]); goto finish; } argv[optind] = command; } if (!arg_description) { description = strv_join(argv + optind, " "); if (!description) { r = log_oom(); goto finish; } if (arg_unit && isempty(description)) { r = free_and_strdup(&description, arg_unit); if (r < 0) goto finish; } arg_description = description; } /* If --wait is used connect via the bus, unconditionally, as ref/unref is not supported via the limited direct * connection */ if (arg_wait) r = bus_connect_transport(arg_transport, arg_host, arg_user, &bus); else r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus); if (r < 0) { log_error_errno(r, "Failed to create bus connection: %m"); goto finish; } if (arg_scope) r = start_transient_scope(bus, argv + optind); else if (with_timer()) r = start_transient_timer(bus, argv + optind); else r = start_transient_service(bus, argv + optind, &retval); finish: strv_free(arg_environment); strv_free(arg_property); strv_free(arg_timer_property); return r < 0 ? EXIT_FAILURE : retval; }