+static void warn_wall(usec_t n, struct sd_shutdown_command *c) {
+ char date[FORMAT_TIMESTAMP_MAX];
+ const char *prefix;
+ char *l = NULL;
+
+ assert(c);
+ assert(c->warn_wall);
+
+ if (n >= c->usec)
+ return;
+
+ if (c->mode == SD_SHUTDOWN_HALT)
+ prefix = "The system is going down for system halt at ";
+ else if (c->mode == SD_SHUTDOWN_POWEROFF)
+ prefix = "The system is going down for power-off at ";
+ else if (c->mode == SD_SHUTDOWN_REBOOT)
+ prefix = "The system is going down for reboot at ";
+ else if (c->mode == SD_SHUTDOWN_KEXEC)
+ prefix = "The system is going down for kexec reboot at ";
+ else
+ assert_not_reached("Unknown mode!");
+
+ if (asprintf(&l, "%s%s%s%s!", c->wall_message, c->wall_message[0] ? "\n" : "",
+ prefix, format_timestamp(date, sizeof(date), c->usec)) < 0)
+ log_error("Failed to allocate wall message");
+ else {
+ utmp_wall(l, NULL);
+ free(l);
+ }
+}
+
+static usec_t when_wall(usec_t n, usec_t elapse) {
+
+ static const struct {
+ usec_t delay;
+ usec_t interval;
+ } table[] = {
+ { 10 * USEC_PER_MINUTE, USEC_PER_MINUTE },
+ { USEC_PER_HOUR, 15 * USEC_PER_MINUTE },
+ { 3 * USEC_PER_HOUR, 30 * USEC_PER_MINUTE }
+ };
+
+ usec_t left, sub;
+ unsigned i;
+
+ /* If the time is already passed, then don't announce */
+ if (n >= elapse)
+ return 0;
+
+ left = elapse - n;
+ for (i = 0; i < ELEMENTSOF(table); i++)
+ if (n + table[i].delay >= elapse) {
+ sub = ((left / table[i].interval) * table[i].interval);
+ break;
+ }
+
+ if (i >= ELEMENTSOF(table))
+ sub = ((left / USEC_PER_HOUR) * USEC_PER_HOUR);
+
+ return elapse > sub ? elapse - sub : 1;
+}
+
+static usec_t when_nologin(usec_t elapse) {
+ return elapse > 5*USEC_PER_MINUTE ? elapse - 5*USEC_PER_MINUTE : 1;
+}
+
+static const char *mode_to_string(enum sd_shutdown_mode m) {
+ switch (m) {
+ case SD_SHUTDOWN_REBOOT:
+ return "reboot";
+ case SD_SHUTDOWN_POWEROFF:
+ return "poweroff";
+ case SD_SHUTDOWN_HALT:
+ return "halt";
+ case SD_SHUTDOWN_KEXEC:
+ return "kexec";
+ default:
+ return NULL;
+ }
+}
+
+static int update_schedule_file(struct sd_shutdown_command *c) {
+ int r;
+ FILE *f;
+ char *temp_path, *t;
+
+ assert(c);
+
+ r = safe_mkdir("/run/systemd/shutdown", 0755, 0, 0);
+ if (r < 0) {
+ log_error("Failed to create shutdown subdirectory: %s", strerror(-r));
+ return r;
+ }
+
+ t = cescape(c->wall_message);
+ if (!t) {
+ log_error("Out of memory");
+ return -ENOMEM;
+ }
+
+ r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path);
+ if (r < 0) {
+ log_error("Failed to save information about scheduled shutdowns: %s", strerror(-r));
+ free(t);
+ return r;
+ }
+
+ fchmod(fileno(f), 0644);
+
+ fprintf(f,
+ "USEC=%llu\n"
+ "WARN_WALL=%i\n"
+ "MODE=%s\n",
+ (unsigned long long) c->usec,
+ c->warn_wall,
+ mode_to_string(c->mode));
+
+ if (c->dry_run)
+ fputs("DRY_RUN=1\n", f);
+
+ if (!isempty(t))
+ fprintf(f, "WALL_MESSAGE=%s\n", t);
+
+ free(t);
+
+ fflush(f);
+
+ if (ferror(f) || rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) {
+ log_error("Failed to write information about scheduled shutdowns: %m");
+ r = -errno;
+
+ unlink(temp_path);
+ unlink("/run/systemd/shutdown/scheduled");
+ }
+
+ fclose(f);
+ free(temp_path);
+
+ return r;
+}
+
+static bool scheduled(struct sd_shutdown_command *c) {
+ return c->usec > 0 && c->mode != SD_SHUTDOWN_NONE;
+}
+