static void test_one(const char *input, const char *output) { CalendarSpec *c; _cleanup_free_ char *p = NULL, *q = NULL; usec_t u; char buf[FORMAT_TIMESTAMP_MAX]; int r; assert_se(calendar_spec_from_string(input, &c) >= 0); assert_se(calendar_spec_to_string(c, &p) >= 0); printf("\"%s\" → \"%s\"\n", input, p); assert_se(streq(p, output)); u = now(CLOCK_REALTIME); r = calendar_spec_next_usec(c, u, &u); printf("Next: %s\n", r < 0 ? strerror(-r) : format_timestamp(buf, sizeof(buf), u)); calendar_spec_free(c); assert_se(calendar_spec_from_string(p, &c) >= 0); assert_se(calendar_spec_to_string(c, &q) >= 0); calendar_spec_free(c); assert_se(streq(q, p)); }
static void test_timestamp(void) { char buf[FORMAT_TIMESTAMP_MAX]; _cleanup_free_ char *t = NULL; CalendarSpec *c; usec_t x, y; /* Ensure that a timestamp is also a valid calendar specification. Convert forth and back */ x = now(CLOCK_REALTIME); assert_se(format_timestamp_us(buf, sizeof(buf), x)); printf("%s\n", buf); assert_se(calendar_spec_from_string(buf, &c) >= 0); assert_se(calendar_spec_to_string(c, &t) >= 0); calendar_spec_free(c); printf("%s\n", t); assert_se(parse_timestamp(t, &y) >= 0); assert_se(y == x); }
static void test_hourly_bug_4031(void) { CalendarSpec *c; usec_t n, u, w; char buf[FORMAT_TIMESTAMP_MAX], zaf[FORMAT_TIMESTAMP_MAX]; int r; assert_se(calendar_spec_from_string("hourly", &c) >= 0); n = now(CLOCK_REALTIME); assert_se((r = calendar_spec_next_usec(c, n, &u)) >= 0); printf("Now: %s (%"PRIu64")\n", format_timestamp_us(buf, sizeof buf, n), n); printf("Next hourly: %s (%"PRIu64")\n", r < 0 ? strerror(-r) : format_timestamp_us(buf, sizeof buf, u), u); assert_se((r = calendar_spec_next_usec(c, u, &w)) >= 0); printf("Next hourly: %s (%"PRIu64")\n", r < 0 ? strerror(-r) : format_timestamp_us(zaf, sizeof zaf, w), w); assert_se(n < u); assert_se(u <= n + USEC_PER_HOUR); assert_se(u < w); assert_se(w <= u + USEC_PER_HOUR); calendar_spec_free(c); }
static void test_next(const char *input, const char *new_tz, usec_t after, usec_t expect) { CalendarSpec *c; usec_t u; char *old_tz; char buf[FORMAT_TIMESTAMP_MAX]; int r; old_tz = getenv("TZ"); if (old_tz) old_tz = strdupa(old_tz); if (new_tz) assert_se(setenv("TZ", new_tz, 1) >= 0); else assert_se(unsetenv("TZ") >= 0); tzset(); assert_se(calendar_spec_from_string(input, &c) >= 0); printf("\"%s\"\n", input); u = after; r = calendar_spec_next_usec(c, after, &u); printf("At: %s\n", r < 0 ? strerror(-r) : format_timestamp_us(buf, sizeof buf, u)); if (expect != (usec_t)-1) assert_se(r >= 0 && u == expect); else assert(r == -ENOENT); calendar_spec_free(c); if (old_tz) assert_se(setenv("TZ", old_tz, 1) >= 0); else assert_se(unsetenv("TZ") >= 0); tzset(); }
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[]) { CalendarSpec *c; test_one("Sat,Thu,Mon-Wed,Sat-Sun", "Mon..Thu,Sat,Sun *-*-* 00:00:00"); test_one("Sat,Thu,Mon..Wed,Sat..Sun", "Mon..Thu,Sat,Sun *-*-* 00:00:00"); test_one("Mon,Sun 12-*-* 2,1:23", "Mon,Sun 2012-*-* 01,02:23:00"); test_one("Wed *-1", "Wed *-*-01 00:00:00"); test_one("Wed-Wed,Wed *-1", "Wed *-*-01 00:00:00"); test_one("Wed..Wed,Wed *-1", "Wed *-*-01 00:00:00"); test_one("Wed, 17:48", "Wed *-*-* 17:48:00"); test_one("Wednesday,", "Wed *-*-* 00:00:00"); test_one("Wed-Sat,Tue 12-10-15 1:2:3", "Tue..Sat 2012-10-15 01:02:03"); test_one("Wed..Sat,Tue 12-10-15 1:2:3", "Tue..Sat 2012-10-15 01:02:03"); test_one("*-*-7 0:0:0", "*-*-07 00:00:00"); test_one("10-15", "*-10-15 00:00:00"); test_one("monday *-12-* 17:00", "Mon *-12-* 17:00:00"); test_one("Mon,Fri *-*-3,1,2 *:30:45", "Mon,Fri *-*-01..03 *:30:45"); test_one("12,14,13,12:20,10,30", "*-*-* 12..14:10,20,30:00"); test_one("mon,fri *-1/2-1,3 *:30:45", "Mon,Fri *-01/2-01,03 *:30:45"); test_one("03-05 08:05:40", "*-03-05 08:05:40"); test_one("08:05:40", "*-*-* 08:05:40"); test_one("05:40", "*-*-* 05:40:00"); test_one("Sat,Sun 12-05 08:05:40", "Sat,Sun *-12-05 08:05:40"); test_one("Sat,Sun 08:05:40", "Sat,Sun *-*-* 08:05:40"); test_one("2003-03-05 05:40", "2003-03-05 05:40:00"); test_one("2003-03-05", "2003-03-05 00:00:00"); test_one("03-05", "*-03-05 00:00:00"); test_one("hourly", "*-*-* *:00:00"); test_one("daily", "*-*-* 00:00:00"); test_one("monthly", "*-*-01 00:00:00"); test_one("weekly", "Mon *-*-* 00:00:00"); test_one("minutely", "*-*-* *:*:00"); test_one("quarterly", "*-01,04,07,10-01 00:00:00"); test_one("semi-annually", "*-01,07-01 00:00:00"); test_one("annually", "*-01-01 00:00:00"); test_one("*:2/3", "*-*-* *:02/3:00"); test_one("2015-10-25 01:00:00 uTc", "2015-10-25 01:00:00 UTC"); test_one("2016-03-27 03:17:00.4200005", "2016-03-27 03:17:00.420001"); test_one("2016-03-27 03:17:00/0.42", "2016-03-27 03:17:00/0.420000"); test_one("2016-03-27 03:17:00/0.42", "2016-03-27 03:17:00/0.420000"); test_one("9..11,13:00,30", "*-*-* 09..11,13:00,30:00"); test_one("1..3-1..3 1..3:1..3", "*-01..03-01..03 01..03:01..03:00"); test_one("00:00:1.125..2.125", "*-*-* 00:00:01.125000,02.125000"); test_one("00:00:1.0..3.8", "*-*-* 00:00:01..03"); test_one("00:00:01..03", "*-*-* 00:00:01..03"); test_one("00:00:01/2,02..03", "*-*-* 00:00:01/2,02,03"); test_one("*-*~1 Utc", "*-*~01 00:00:00 UTC"); test_one("*-*~05,3 ", "*-*~03,05 00:00:00"); test_one("*-*~* 00:00:00", "*-*-* 00:00:00"); test_one("Monday", "Mon *-*-* 00:00:00"); test_one("Monday *-*-*", "Mon *-*-* 00:00:00"); test_one("*-*-*", "*-*-* 00:00:00"); test_one("*:*:*", "*-*-* *:*:*"); test_one("*:*", "*-*-* *:*:00"); test_one("12:*", "*-*-* 12:*:00"); test_one("*:30", "*-*-* *:30:00"); test_next("2016-03-27 03:17:00", "", 12345, 1459048620000000); test_next("2016-03-27 03:17:00", "CET", 12345, 1459041420000000); test_next("2016-03-27 03:17:00", "EET", 12345, -1); test_next("2016-03-27 03:17:00 UTC", NULL, 12345, 1459048620000000); test_next("2016-03-27 03:17:00 UTC", "", 12345, 1459048620000000); test_next("2016-03-27 03:17:00 UTC", "CET", 12345, 1459048620000000); test_next("2016-03-27 03:17:00 UTC", "EET", 12345, 1459048620000000); test_next("2016-03-27 03:17:00.420000001 UTC", "EET", 12345, 1459048620420000); test_next("2016-03-27 03:17:00.4200005 UTC", "EET", 12345, 1459048620420001); test_next("2015-11-13 09:11:23.42", "EET", 12345, 1447398683420000); test_next("2015-11-13 09:11:23.42/1.77", "EET", 1447398683420000, 1447398685190000); test_next("2015-11-13 09:11:23.42/1.77", "EET", 1447398683419999, 1447398683420000); test_next("Sun 16:00:00", "CET", 1456041600123456, 1456066800000000); test_next("*-04-31", "", 12345, -1); test_next("2016-02~01 UTC", "", 12345, 1456704000000000); test_next("Mon 2017-05~01..07 UTC", "", 12345, 1496016000000000); test_next("Mon 2017-05~07/1 UTC", "", 12345, 1496016000000000); assert_se(calendar_spec_from_string("test", &c) < 0); assert_se(calendar_spec_from_string(" utc", &c) < 0); assert_se(calendar_spec_from_string(" ", &c) < 0); assert_se(calendar_spec_from_string("", &c) < 0); assert_se(calendar_spec_from_string("7", &c) < 0); assert_se(calendar_spec_from_string("121212:1:2", &c) < 0); assert_se(calendar_spec_from_string("2000-03-05.23 00:00:00", &c) < 0); assert_se(calendar_spec_from_string("2000-03-05 00:00.1:00", &c) < 0); assert_se(calendar_spec_from_string("00:00:00/0.00000001", &c) < 0); assert_se(calendar_spec_from_string("00:00:00.0..00.9", &c) < 0); assert_se(calendar_spec_from_string("2016~11-22", &c) < 0); assert_se(calendar_spec_from_string("*-*~5/5", &c) < 0); assert_se(calendar_spec_from_string("Monday.. 12:00", &c) < 0); assert_se(calendar_spec_from_string("Monday..", &c) < 0); assert_se(calendar_spec_from_string("-00:+00/-5", &c) < 0); assert_se(calendar_spec_from_string("00:+00/-5", &c) < 0); assert_se(calendar_spec_from_string("2016- 11- 24 12: 30: 00", &c) < 0); test_timestamp(); test_hourly_bug_4031(); return 0; }