logo

cmd-timer

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

timer.c (3053B)


  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 <stdio.h> // fprintf, fputs
  10. #include <stdlib.h> // exit
  11. #include <string.h> // strerror
  12. #include <sys/wait.h> // waitpid
  13. #include <time.h> // timer_create
  14. #include <unistd.h> // sleep
  15. extern char **environ;
  16. static char **args;
  17. const char *argv0 = "timer";
  18. static void
  19. sig_timer(int sig)
  20. {
  21. pid_t child = -1;
  22. errno = 0;
  23. int ret = posix_spawnp(&child, args[0], NULL, NULL, args, environ);
  24. if(ret != 0)
  25. {
  26. fprintf(stderr, "timer: error: Failed spawning command '%s': %s\n", args[0], strerror(ret));
  27. exit(1);
  28. }
  29. }
  30. static void
  31. sig_chld(int sig)
  32. {
  33. int chld_stat = 0;
  34. waitpid((pid_t)-1, &chld_stat, WNOHANG);
  35. if(!WIFEXITED(chld_stat)) return;
  36. if(WEXITSTATUS(chld_stat) != 0) exit(WEXITSTATUS(chld_stat));
  37. }
  38. static void
  39. bad_usage()
  40. {
  41. fputs("Usage: timer <interval> <command> [arguments...]\n", stderr);
  42. exit(1);
  43. }
  44. int
  45. main(int argc, char *argv[])
  46. {
  47. argc--;
  48. argv++;
  49. if(argc < 2)
  50. {
  51. fprintf(stderr, "timer: error: Got '%d' arguments instead of at least 2\n", argc);
  52. bad_usage();
  53. }
  54. struct timespec dur = {
  55. .tv_sec = 0,
  56. .tv_nsec = 0,
  57. };
  58. if(strtodur(*argv, &dur) != 0) return 1;
  59. if(dur.tv_sec == 0 && dur.tv_nsec == 0)
  60. {
  61. fputs("timer: error: Got a duration of 0\n", stderr);
  62. return 1;
  63. }
  64. argc--;
  65. argv++;
  66. struct sigaction sa_alrm;
  67. sa_alrm.sa_handler = &sig_timer;
  68. sigemptyset(&sa_alrm.sa_mask);
  69. sa_alrm.sa_flags = SA_RESTART;
  70. if(sigaction(SIGALRM, &sa_alrm, NULL) != 0)
  71. {
  72. fprintf(stderr, "timer: error: Failed registering signal handler: %s\n", strerror(errno));
  73. return 1;
  74. }
  75. struct sigaction sa_chld;
  76. sa_chld.sa_handler = &sig_chld;
  77. sigemptyset(&sa_chld.sa_mask);
  78. sa_chld.sa_flags = SA_RESTART;
  79. if(sigaction(SIGCHLD, &sa_chld, NULL) != 0)
  80. {
  81. fprintf(stderr, "timer: error: Failed registering signal handler: %s\n", strerror(errno));
  82. return 1;
  83. }
  84. struct sigevent timer_se;
  85. timer_se.sigev_signo = SIGALRM;
  86. timer_se.sigev_notify = SIGEV_SIGNAL;
  87. timer_t timer;
  88. if(timer_create(0, &timer_se, &timer) != 0)
  89. {
  90. fprintf(stderr, "timer: error: Failed creating timer: %s\n", strerror(errno));
  91. return 1;
  92. }
  93. struct itimerspec timerspec = {
  94. .it_value = dur,
  95. .it_interval = dur,
  96. };
  97. if(timer_settime(timer, 0, &timerspec, NULL) != 0)
  98. {
  99. fprintf(stderr, "timer: error: Failed setting timer: %s\n", strerror(errno));
  100. return 1;
  101. }
  102. args = argv;
  103. sigset_t sigmask;
  104. sigfillset(&sigmask);
  105. if(sigdelset(&sigmask, SIGALRM) != 0)
  106. {
  107. fprintf(stderr, "timer: error: Failed adding SIGALARM to new sigmask: %s\n", strerror(errno));
  108. return 1;
  109. }
  110. if(sigdelset(&sigmask, SIGCHLD) != 0)
  111. {
  112. fprintf(stderr, "timer: error: Failed adding SIGCHLD to new sigmask: %s\n", strerror(errno));
  113. return 1;
  114. }
  115. while(sigsuspend(&sigmask))
  116. ;
  117. return 0;
  118. }