44#include <sys/types.h>
55#include <sys/wait.h>
66#include <sys/prctl.h>
7+ #include <sys/stat.h>
78
89#include <errno.h>
910#include <signal.h>
1718#include "tiniConfig.h"
1819#include "tiniLicense.h"
1920
21+ extern char * optarg ;
22+
2023#if TINI_MINIMAL
2124#define PRINT_FATAL (...) fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n");
2225#define PRINT_WARNING (...) if (verbosity > 0) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); }
@@ -41,15 +44,25 @@ typedef struct {
4144 struct sigaction * const sigttou_action_ptr ;
4245} signal_configuration_t ;
4346
47+ struct file_change_t {
48+ const char * filename ;
49+ dev_t last_dev ;
50+ ino_t last_ino ;
51+ time_t last_mtime ;
52+ time_t last_ctime ;
53+ struct file_change_t * next ;
54+ };
55+ typedef struct file_change_t file_change_t ;
56+
4457static unsigned int verbosity = DEFAULT_VERBOSITY ;
4558
4659#ifdef PR_SET_CHILD_SUBREAPER
4760#define HAS_SUBREAPER 1
48- #define OPT_STRING "hsvgl "
61+ #define OPT_STRING "hsvglS:F: "
4962#define SUBREAPER_ENV_VAR "TINI_SUBREAPER"
5063#else
5164#define HAS_SUBREAPER 0
52- #define OPT_STRING "hvgl "
65+ #define OPT_STRING "hvglS:F: "
5366#endif
5467
5568#define VERBOSITY_ENV_VAR "TINI_VERBOSITY"
@@ -62,6 +75,9 @@ static unsigned int subreaper = 0;
6275#endif
6376static unsigned int kill_process_group = 0 ;
6477
78+ static unsigned int file_change_signal = 1 ; /* HUP */
79+ static file_change_t * file_change_files = NULL ;
80+
6581static struct timespec ts = { .tv_sec = 1 , .tv_nsec = 0 };
6682
6783static const char reaper_warning [] = "Tini is not running as PID 1 "
@@ -196,6 +212,10 @@ void print_usage(char* const name, FILE* const file) {
196212#endif
197213 fprintf (file , " -v: Generate more verbose output. Repeat up to 3 times.\n" );
198214 fprintf (file , " -g: Send signals to the child's process group.\n" );
215+ fprintf (file , " -S signal: numeric signal to send to child if '-F' files\n"
216+ " change. (default: 1 => HUP)\n" );
217+ fprintf (file , " -F path: file(s) to check for changes for reload signal\n"
218+ " (may be specified more than once).\n" );
199219 fprintf (file , " -l: Show license and exit.\n" );
200220#endif
201221
@@ -220,8 +240,47 @@ void print_license(FILE* const file) {
220240 }
221241}
222242
243+ void add_file_change (char * const filename ) {
244+ file_change_t * new = NULL , * curr ;
245+ struct stat file_st ;
246+
247+ // Here we add to a linked list of file_change_t structures
248+ // we layout each "structure" as <struct><filename><nul> in
249+ // the RAM. We expect only a very few - so a linked list here
250+ // is fine.
251+ new = malloc (sizeof (file_change_t ) + 1 + strlen (filename ));
252+ new -> next = NULL ;
253+ new -> filename = (const char * )(new + 1 );
254+ // we break the const once...
255+ strcpy ((char * )(new -> filename ), filename );
256+
257+ if (stat (new -> filename , & file_st ) == 0 ) {
258+ new -> last_dev = file_st .st_dev ;
259+ new -> last_ino = file_st .st_ino ;
260+ new -> last_mtime = file_st .st_mtime ;
261+ new -> last_ctime = file_st .st_ctime ;
262+ } else {
263+ // we produce blank records for files that don't
264+ // exist or are otherwise not accessible to us,
265+ // this way, if they become accessible, we also
266+ // signal the reload.
267+ new -> last_dev = 0 ;
268+ new -> last_ino = 0 ;
269+ new -> last_mtime = 0 ;
270+ new -> last_ctime = 0 ;
271+ }
272+
273+ if (!file_change_files ) {
274+ file_change_files = new ;
275+ } else {
276+ for (curr = file_change_files ; curr -> next != NULL ; curr = curr -> next );
277+ curr -> next = new ;
278+ }
279+ }
280+
223281int parse_args (const int argc , char * const argv [], char * (* * child_args_ptr_ptr )[], int * const parse_fail_exitcode_ptr ) {
224282 char * name = argv [0 ];
283+ int tmpi ;
225284
226285 // We handle --version if it's the *only* argument provided.
227286 if (argc == 2 && strcmp ("--version" , argv [1 ]) == 0 ) {
@@ -251,6 +310,19 @@ int parse_args(const int argc, char* const argv[], char* (**child_args_ptr_ptr)[
251310 kill_process_group ++ ;
252311 break ;
253312
313+ case 'S' :
314+ tmpi = atoi (optarg );
315+ if (tmpi == 0 ) {
316+ print_usage (name , stderr );
317+ return 1 ;
318+ }
319+ file_change_signal = tmpi ;
320+ break ;
321+
322+ case 'F' :
323+ add_file_change (optarg );
324+ break ;
325+
254326 case 'l' :
255327 print_license (stdout );
256328 * parse_fail_exitcode_ptr = 0 ;
@@ -484,6 +556,45 @@ int reap_zombies(const pid_t child_pid, int* const child_exitcode_ptr) {
484556}
485557
486558
559+ int kill_on_change_files (pid_t child_pid ) {
560+ file_change_t * curr ;
561+ struct stat file_st ;
562+ char changed = 0 ;
563+
564+ // We go through our linked list and figure out what changed
565+ for (curr = file_change_files ; curr != NULL ; curr = curr -> next ) {
566+ if (stat (curr -> filename , & file_st ) == 0 ) {
567+ if ( curr -> last_dev != file_st .st_dev ||
568+ curr -> last_ino != file_st .st_ino ||
569+ curr -> last_mtime != file_st .st_mtime ||
570+ curr -> last_ctime != file_st .st_ctime ||
571+ 0 ) {
572+ PRINT_DEBUG ("Found new/changed file: %s" , curr -> filename );
573+ changed = 1 ;
574+ curr -> last_dev = file_st .st_dev ;
575+ curr -> last_ino = file_st .st_ino ;
576+ curr -> last_mtime = file_st .st_mtime ;
577+ curr -> last_ctime = file_st .st_ctime ;
578+ }
579+ } else if (curr -> last_ctime != 0 ) {
580+ PRINT_DEBUG ("Found deleted file: %s" , curr -> filename );
581+ changed = 1 ;
582+ curr -> last_dev = 0 ;
583+ curr -> last_ino = 0 ;
584+ curr -> last_mtime = 0 ;
585+ curr -> last_ctime = 0 ;
586+ }
587+ }
588+
589+ if (changed ) {
590+ PRINT_INFO ("files changed, killing %d with %d" , child_pid , file_change_signal );
591+ kill (kill_process_group ? - child_pid : child_pid , file_change_signal );
592+ }
593+
594+ return 0 ;
595+ }
596+
597+
487598int main (int argc , char * argv []) {
488599 pid_t child_pid ;
489600
@@ -542,6 +653,11 @@ int main(int argc, char *argv[]) {
542653 return 1 ;
543654 }
544655
656+ /* check the files for changes */
657+ if (file_change_files && kill_on_change_files (child_pid )) {
658+ return 1 ;
659+ }
660+
545661 /* Now, reap zombies */
546662 if (reap_zombies (child_pid , & child_exitcode )) {
547663 return 1 ;
0 commit comments