logo

cmd-timer

run command at a specific interval git clone https://anongit.hacktivis.me/git/cmd-timer.git

timer.c (3592B)


  1. // SPDX-FileCopyrightText: 2025 Haelwenn (lanodan) Monnier <contact+cmd-timer@hacktivis.me>
  2. // SPDX-License-Identifier: MPL-2.0
  3. #define _POSIX_C_SOURCE 200809L
  4. #include "strtodur.h"
  5. #include <errno.h>
  6. #include <limits.h> // UINT_MAX
  7. #include <signal.h> // sigaction
  8. #include <spawn.h> // posix_spawnp
  9. #include <stdbool.h>
  10. #include <stdio.h> // fprintf, fputs
  11. #include <stdlib.h> // exit
  12. #include <string.h> // strerror
  13. #include <sys/wait.h> // waitpid
  14. #include <time.h> // timer_create
  15. #include <unistd.h> // sleep, getopt
  16. extern char **environ;
  17. static char **args;
  18. const char *argv0 = "timer";
  19. static void
  20. sig_timer(int sig)
  21. {
  22. pid_t child = -1;
  23. errno = 0;
  24. int ret = posix_spawnp(&child, args[0], NULL, NULL, args, environ);
  25. if(ret != 0)
  26. {
  27. fprintf(stderr, "timer: error: Failed spawning command '%s': %s\n", args[0], strerror(ret));
  28. exit(1);
  29. }
  30. }
  31. static void
  32. sig_chld(int sig)
  33. {
  34. int chld_stat = 0;
  35. waitpid((pid_t)-1, &chld_stat, WNOHANG);
  36. if(!WIFEXITED(chld_stat)) return;
  37. if(WEXITSTATUS(chld_stat) != 0) exit(WEXITSTATUS(chld_stat));
  38. }
  39. static void
  40. bad_usage()
  41. {
  42. fputs("Usage: timer [-w] <interval> <command> [arguments...]\n", stderr);
  43. exit(1);
  44. }
  45. static void
  46. timer_errx(int err, char *msg)
  47. {
  48. fprintf(stderr, "timer: error: %s: %s\n", msg, strerror(errno));
  49. exit(err);
  50. }
  51. int
  52. main(int argc, char *argv[])
  53. {
  54. bool opt_w = false;
  55. for(char c = -1; (c = getopt(argc, argv, ":w")) != -1;)
  56. {
  57. switch(c)
  58. {
  59. case 'w':
  60. opt_w = true;
  61. break;
  62. default:
  63. fprintf(stderr, "timer: error: Unhandled option -%c\n", optopt);
  64. return 1;
  65. }
  66. }
  67. argc -= optind;
  68. argv += optind;
  69. if(argc < 2)
  70. {
  71. fprintf(stderr, "timer: error: Got '%d' arguments instead of at least 2\n", argc);
  72. bad_usage();
  73. }
  74. struct timespec dur = {
  75. .tv_sec = 0,
  76. .tv_nsec = 0,
  77. };
  78. if(strtodur(*argv, &dur) != 0) return 1;
  79. if(dur.tv_sec == 0 && dur.tv_nsec == 0)
  80. {
  81. fputs("timer: error: Got a duration of 0\n", stderr);
  82. return 1;
  83. }
  84. argc--;
  85. argv++;
  86. struct sigaction sa_alrm;
  87. sa_alrm.sa_handler = &sig_timer;
  88. sigemptyset(&sa_alrm.sa_mask);
  89. sa_alrm.sa_flags = SA_RESTART;
  90. if(sigaction(SIGALRM, &sa_alrm, NULL) != 0)
  91. {
  92. fprintf(stderr, "timer: error: Failed registering signal handler: %s\n", strerror(errno));
  93. return 1;
  94. }
  95. struct sigaction sa_chld;
  96. sa_chld.sa_handler = &sig_chld;
  97. sigemptyset(&sa_chld.sa_mask);
  98. sa_chld.sa_flags = SA_RESTART;
  99. if(sigaction(SIGCHLD, &sa_chld, NULL) != 0)
  100. {
  101. fprintf(stderr, "timer: error: Failed registering signal handler: %s\n", strerror(errno));
  102. return 1;
  103. }
  104. struct sigevent timer_se;
  105. timer_se.sigev_signo = SIGALRM;
  106. timer_se.sigev_notify = SIGEV_SIGNAL;
  107. timer_t timer;
  108. if(timer_create(0, &timer_se, &timer) != 0)
  109. {
  110. fprintf(stderr, "timer: error: Failed creating timer: %s\n", strerror(errno));
  111. return 1;
  112. }
  113. struct itimerspec timerspec = {
  114. .it_value = dur,
  115. .it_interval = dur,
  116. };
  117. if(timer_settime(timer, 0, &timerspec, NULL) != 0)
  118. {
  119. fprintf(stderr, "timer: error: Failed setting timer: %s\n", strerror(errno));
  120. return 1;
  121. }
  122. args = argv;
  123. sigset_t sigmask;
  124. sigfillset(&sigmask);
  125. if(sigdelset(&sigmask, SIGALRM) != 0)
  126. timer_errx(1, "Failed adding SIGALRM to sigsuspend sigmask");
  127. if(sigdelset(&sigmask, SIGCHLD) != 0)
  128. timer_errx(1, "Failed adding SIGCHLD to sigsuspend sigmask");
  129. if(sigdelset(&sigmask, SIGINT) != 0) timer_errx(1, "Failed adding SIGINT to sigsuspend sigmask");
  130. if(sigdelset(&sigmask, SIGTERM) != 0)
  131. timer_errx(1, "Failed adding SIGTERM to sigsuspend sigmask");
  132. if(!opt_w) sig_timer(0);
  133. while(sigsuspend(&sigmask))
  134. ;
  135. return 0;
  136. }