]> rtime.felk.cvut.cz Git - l4.git/blob - l4/tool/gendep/deptrack.c
update
[l4.git] / l4 / tool / gendep / deptrack.c
1 /*
2   deptrack.c -- high-level routines for gendepend.
3
4   (c) Han-Wen Nienhuys <hanwen@cs.uu.nl> 1998
5   (c) Michael Roitzsch <mroi@os.inf.tu-dresden.de> 2007
6  */
7
8 #include <stdio.h>
9 #include <string.h>
10 #include <fcntl.h>
11 #include <regex.h>
12 #include <errno.h>
13 #include <stdlib.h>
14 #include <assert.h>
15 #include <unistd.h>
16
17 #if defined(__APPLE__) || defined(__FreeBSD__)
18 #include <sys/types.h>
19 #include <sys/sysctl.h>
20 #endif
21
22 #include "gendep.h"
23
24 #ifndef min
25 #define min(a,b) ((a)<(b)?(a):(b))
26 #endif
27
28 extern int errno;
29
30 static FILE * output;
31 static struct
32 {
33   regex_t *re_array;
34   char *invert_array;
35   int max, no;
36 } regexps;
37
38 static char * executable_name;
39 static char * wanted_executable_name;
40 static char * target;
41 static char * depfile_name;
42
43 /* Special mode: Detection mode. In this mode, we watch for a binary opening
44 a file specified in the environment variable GENDEP_SOURCE (stored in
45 target). The binary name is written into $(GENDEP_OUTPUT) (depfile_name)
46 then. */
47 static int detection_mode;
48 static int detection_done;
49
50 /* List of dependencies. We first collect all, then write them out. */
51 static struct strlist{
52         const char*name;
53         struct strlist *next;
54 } *dependencies; 
55
56 #define STRLEN 1024
57
58 /*
59   simplistic wordwrap
60  */
61 static void
62 write_word (char const * word)
63 {
64   static int column;
65   int l = strlen (word);
66   if (l + column > 75 && column>1)
67     {
68       fputs ("\\\n\t",output);
69       column = 8;
70     }
71
72   column += l;
73   fputs (word, output);
74   fputs (" ", output);
75 }
76
77 static void
78 xnomem(void *p)
79 {
80   if (!p)
81     {
82       errno = ENOMEM;
83       perror ("libgendep.so");
84     }
85 }
86
87 static char *
88 xstrdup (char *s)
89 {
90   char *c = strdup (s);
91   xnomem (c);
92   return c;
93 }
94
95 static void *
96 xrealloc (void *s, int sz)
97 {
98   void *c = realloc (s, sz);
99   xnomem (c);
100   return c;
101 }
102
103 static void *
104 xmalloc (int sz)
105 {
106   void *c;
107   c = malloc (sz);
108   xnomem (c);
109   return c;
110 }
111
112 /*!\brief Trim the filename
113  *
114  * Remove redundant "./" at the beginning of a filename.
115  *
116  * We are not very minimal about memory usage, because this would require
117  * scanning the filename twice, instead of once.
118  */
119 static char* trim (const char*fn){
120   char *name;
121
122   /* remove trailing "./" */
123   while(fn[0]=='.' && fn[1]=='/') fn+=2;
124   name = xmalloc(strlen(fn)+1);
125   if(name==0) return 0;
126
127   strcpy(name, fn);
128   return name;
129 }
130
131 /*!\brief add a dependency
132  *
133  * Add a dependency to the list of dependencies for the current target.
134  * If the name is already registered, it won't be added.
135  */
136 static void gendep__adddep(const char*name){
137   struct strlist *dep;
138   char *namecopy;
139
140   if (!(namecopy = trim(name)))
141     return;
142
143   /* special detection mode ? */
144   if (detection_mode)
145     {
146       if (!strcmp(namecopy, target))
147         detection_done = 1;
148
149       free(namecopy);
150       return;
151     }
152
153   if (!strcmp(target, name))
154     return;
155
156   for(dep=dependencies; dep; dep=dep->next){
157     if(!strcmp(dep->name, namecopy)){
158             free(namecopy);
159             return;
160         }
161   }
162   dep = xmalloc(sizeof(struct strlist));
163   dep->name = namecopy;
164   dep->next = dependencies;
165   dependencies = dep;
166 }
167
168 /*!\brief Delete a dependency
169  *
170  * Delete a dependency from the list of dependencies for the current target.
171  * This is used if an already registered file is removed later.
172  */
173 static void gendep__deldep(const char*name){
174   struct strlist *dep, *old;
175   char *namecopy;
176
177   old = 0;
178   if((namecopy = trim(name))==0) return;
179   for(dep=dependencies; dep; dep=dep->next){
180         if(!strcmp(dep->name, namecopy)){
181             if(old) old->next = dep->next;
182             else dependencies=dep->next;
183             free(namecopy);
184             free((char*)dep->name);
185             free(dep);
186             return;
187         }
188         old = dep;
189   }
190   free(namecopy);
191 }
192
193 static int
194 gendep_getenv (char **dest, char *what)
195 {
196   char var[STRLEN]="GENDEP_";
197   strcat (var, what);
198   *dest = getenv (var);
199
200   return !! (*dest);
201 }
202
203 /*
204   Do hairy stuff with regexps and environment variables.
205  */
206 static void
207 setup_regexps (void)
208 {
209   char *regexp_val =0;
210   char *end, *start;
211
212   if(gendep_getenv(&target, "SOURCE")){
213     /* we have to watch for the binary that opens our file first */
214     detection_mode=1;
215     gendep_getenv(&depfile_name, "OUTPUT");
216     return;
217   }
218
219   /* standard mode of operation: catch dependencies */
220   if (!gendep_getenv (&wanted_executable_name, "BINARY"))
221     return;
222
223   if (strcmp (wanted_executable_name, executable_name))
224     {
225       /* support two binary names (e.g. for arch-foo-X and X) */
226       if (!gendep_getenv (&wanted_executable_name, "BINARY_ALT1")
227           || strcmp (wanted_executable_name, executable_name))
228         return;
229     }
230
231   gendep_getenv(&depfile_name, "DEPFILE");
232
233   if (!gendep_getenv (&target, "TARGET"))
234     return;
235
236   if (!gendep_getenv (&regexp_val, executable_name))
237     return;
238
239   /*
240     let's hope malloc (0) does the sensible thing (not return NULL, that is)
241   */
242   regexps.re_array = xmalloc (0);
243   regexps.invert_array = xmalloc (0);
244
245   regexp_val = xstrdup (regexp_val);
246   start = regexp_val;
247   end = regexp_val + strlen (regexp_val);
248
249   /*
250     Duh.  The strength of C : string handling. (not).   Pull apart the whitespace delimited
251     list of regexps, and try to compile them.
252     Be really gnuish with dynamic allocation of arrays. 
253    */
254   do {
255     char * end_re = 0;
256     int in_out;
257     while (*start && *start != '+' && *start != '-')
258       start ++;
259
260     in_out= (*start == '+') ;
261     start ++;
262     end_re = strchr (start, ' ');
263     if (end_re)
264       *end_re = 0;
265
266     if (*start)
267       {
268         regex_t regex;
269         int result= regcomp (&regex, start, REG_NOSUB);
270
271         if (result)
272           {
273             fprintf (stderr, "libgendep.so: Bad regular expression `%s\'\n", start);
274             goto duh;           /* ugh */
275           }
276
277         if (regexps.no >= regexps.max)
278           {
279             /* max_regexps = max_regexps * 2 + 1; */
280             regexps.max ++;
281             regexps.re_array = xrealloc (regexps.re_array, regexps.max * sizeof (regex_t));
282             regexps.invert_array = xrealloc (regexps.invert_array, regexps.max * sizeof (char));        
283           }
284
285         regexps.re_array[regexps.no] = regex;
286         regexps.invert_array[regexps.no++] = in_out;
287       }
288
289   duh:
290     start = end_re ? end_re + 1 :  end;
291   } while (start < end);
292 }
293
294 /*
295   Try to get the name of the binary.  Is there a portable way to do this?
296  */
297 static void get_executable_name(void)
298 {
299   char *basename_p;
300 #ifdef __linux
301   const char * const proc_cmdline = "/proc/self/cmdline";
302   FILE *cmdline = fopen (proc_cmdline, "r");
303   char cmd[STRLEN];
304   int i=0;
305   int c;
306   cmd[STRLEN-1] = 0;
307
308   if (!cmdline)
309     {
310       fprintf(stderr, "libgendep.o: cannot open %s\n", proc_cmdline);
311       exit(-1);
312     }
313
314   while ((c = fgetc(cmdline))!=EOF && c && i < STRLEN-1)
315     cmd[i++] = c;
316
317   cmd[i++] = 0;
318 #elif defined(__APPLE__) || defined(__FreeBSD__)
319   int mib[3], arglen;
320   size_t size;
321   char *procargs, *cmd;
322
323   /* allocate process argument space */
324   mib[0] = CTL_KERN;
325   mib[1] = KERN_ARGMAX;
326   size = sizeof(arglen);
327   if (sysctl(mib, 2, &arglen, &size, NULL, 0) == -1)
328     exit(-1);
329   if (!(procargs = xmalloc(arglen)))
330     exit(-1);
331
332   /* get a copy of the process argument space */
333   mib[0] = CTL_KERN;
334 #if defined(__APPLE__)
335   mib[1] = KERN_PROCARGS2;
336   mib[2] = getpid();
337   size = (size_t)arglen;
338   if (sysctl(mib, 3, procargs, &size, NULL, 0) == -1) {
339     free(procargs);
340     exit(-1);
341   }
342 #else
343   mib[1] = KERN_PROC;
344   mib[2] = KERN_PROC_ARGS;
345   mib[3] = getpid();
346   size = (size_t)arglen;
347   if (sysctl(mib, 4, procargs, &size, NULL, 0) == -1) {
348     free(procargs);
349     exit(-1);
350   }
351 #endif
352
353   /* jump over the argument count */
354   cmd = procargs + sizeof(int);
355 #else
356 #  error "Retrieving the executable name is not implemented for your platform."
357 #endif
358
359   /* ugh.  man 3 basename -> ?  */
360   basename_p =  strrchr(cmd, '/');
361   if (basename_p)
362     basename_p++;
363   else
364     basename_p = cmd;
365
366   executable_name = xstrdup(basename_p);
367
368 #if defined(__APPLE__) || defined(__FreeBSD__)
369   free(procargs);
370 #endif
371 }
372
373 static void initialize (void) __attribute__ ((constructor));
374
375 static void initialize (void)
376 {
377   get_executable_name ();
378   setup_regexps ();
379
380   if (target && !detection_mode)
381     {
382       char fn[STRLEN];
383       char *slash;
384
385       fn[0] = '\0';
386       if(depfile_name){
387           strncpy(fn, depfile_name, STRLEN);
388       } else {
389           slash = strrchr(target, '/');
390           /* copy the path */
391           strncat (fn, target, min(STRLEN, slash?slash-target+1:0));
392           strncat (fn, ".", STRLEN);
393           /* copy the name */
394           strncat(fn, slash?slash+1:target, STRLEN);
395           strncat (fn, ".d", STRLEN);
396       }
397       fn[STRLEN-1]=0;
398
399       if((output = fopen (fn, "w"))==0){
400         fprintf(stderr, "libgendep.so: cannot open %s for writing\n", fn);
401         return;
402       }
403       write_word (target);
404       write_word (":");
405     }
406 }
407
408 /* Someone is opening a file.  If it is opened for reading, and
409   matches the regular expressions, write it to the dep-file.
410
411   Note that we explicitly ignore accesses to /etc/mtab and /proc/...
412   as these files are inspected by libc for Linux and tend to change.
413  */
414 void
415 gendep__register_open (char const *fn, int flags)
416 {
417   if ((detection_mode || output) && !(flags & (O_WRONLY | O_RDWR)))
418     {
419       int i;
420
421       if (fn == strstr(fn, "/etc/mtab") ||
422           fn == strstr(fn, "/proc/"))
423         return;
424
425       for (i = 0; i < regexps.no; i++)
426         {
427           int not_matched = regexec (regexps.re_array +i, fn, 0, NULL, 0);
428
429           if (!(not_matched ^ regexps.invert_array[i]))
430             return;
431         }
432
433       gendep__adddep(fn);
434     }
435 }
436
437 void
438 gendep__register_unlink (char const *fn)
439 {
440   gendep__deldep(fn);
441 }
442
443
444 static void finish (void) __attribute__ ((destructor));
445
446 static void finish (void)
447 {
448   if (output)
449     {
450       struct strlist *dep;
451
452       for(dep=dependencies; dep; dep=dep->next){
453         write_word(dep->name);
454       }
455       fprintf (output, "\n");
456       for(dep=dependencies; dep; dep=dep->next){
457         fputs(dep->name, output);
458         fputs(":\n", output);
459       }
460       fclose (output);
461       return;
462     }
463
464   if (detection_mode && detection_done && depfile_name)
465     {
466       FILE *file;
467
468       /*
469        * libgendep is called in a nested fashion (fork+execve in various
470        * wrappers), so we only write out the most inner occurence, which
471        * means we only write as long as depfile_name is not there
472        */
473       if (access(depfile_name, W_OK) == -1 && errno == ENOENT)
474         {
475           file = fopen(depfile_name, "w");
476           if (!file)
477             return;
478           fwrite(executable_name, strlen(executable_name), 1, file);
479           fclose(file);
480         }
481       return;
482     }
483 }