a1e67733a86acd8881d2c49caaeb726a3668308f
[autobuildrequires.git] / auto-buildrequires-preload.c
1 /* Automatic generation of BuildRequires dependencies for rpmbuild.
2  * Copyright (C) 2008 Red Hat Inc.
3  * Written by Richard W.M. Jones <rjones@redhat.com>
4  * Patches from Rajeesh K Nambiar <rajeeshknambiar@gmail.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20
21 /* This is an LD_PRELOAD extension which logs accesses to any files by
22  * trapping access to any system calls which open files.  Details of
23  * opened files are written to the file named in
24  * the AUTO_BUILDREQUIRES_LOGFILE environment variable.
25  */
26
27 #define _GNU_SOURCE
28
29 #include <stdio.h>
30 #include <limits.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <string.h>
38 #include <dlfcn.h>
39
40 #define ALIAS(ret,syscall,params,brfunc)                                \
41   extern ret syscall params __attribute__((alias (brfunc)))
42
43 static char *br_path (const char *path);
44 static void br_init (void) __attribute__((constructor));
45 static void br_log (const char *fs, ...) __attribute__((format (printf,1,2)));
46
47 /* These are the real glibc symbols, initialized in br_init. */
48 static int (*glibc_open) (const char *pathname, int flags, mode_t mode);
49 static int (*glibc_execve) (const char *filename, char *const argv[],
50                             char *const envp[]);
51
52 /* Canonicalize a relative or absolute path.  If that isn't possible,
53  * return an absolute path.
54  *
55  * The caller must free the result.
56  */
57 static char *
58 br_path (const char *path)
59 {
60   char *dir, *cat, *rp;
61   int len;
62
63   if (path == NULL) return NULL;
64
65   if (path[0] == '/') {
66     rp = realpath (path, NULL);
67     if (!rp) rp = strdup (path);
68     return rp;
69   }
70
71   dir = get_current_dir_name ();
72   if (dir == NULL) return NULL;
73
74   len = strlen (dir) + 1 + strlen (path) + 1;
75   cat = malloc (len);
76   if (cat == NULL) {
77     perror ("malloc");
78     abort ();
79   }
80
81   strcpy (cat, dir);
82   strcat (cat, "/");
83   strcat (cat, path);
84
85   rp = realpath (cat, NULL);
86   free (dir);
87   if (!rp) rp = cat; else free (cat);
88   return rp;
89 }
90
91 int
92 abr_open (const char *pathname, int flags, mode_t mode)
93 {
94   int fd;
95   char *rp;
96
97   rp = br_path (pathname);
98   if (rp)
99     br_log ("open %s\n", rp);
100   else
101     perror (pathname);
102   free (rp);
103
104   fd = glibc_open (pathname, flags, mode);
105   return fd;
106 }
107
108 ALIAS (int, open, (const char *, int, ...), "abr_open");
109
110 int
111 abr_execve (const char *filename, char *const argv[], char *const envp[])
112 {
113   int r;
114   char *rp;
115
116   rp = br_path (filename);
117   if (rp)
118     br_log ("execve %s\n", rp);
119   else
120     perror (filename);
121   free (rp);
122
123   r = glibc_execve (filename, argv, envp);
124   return r;
125 }
126
127 ALIAS (int, execve, (const char *filename, char *const argv[],
128                      char *const envp[]), "abr_execve");
129
130 /* Other syscalls to consider:
131  * access
132  * statfs
133  * stat, lstat
134  * chdir
135  * mkdir
136  * unlink
137  * readlink
138  * 'connect' params can contain a path in some rare circumstances.
139  */
140
141 /* This is the logging function.  We have to be very cautious in this
142  * function and not disturb any part of the process (so far as that is
143  * possible).  eg. When we open a file descriptor we must close it
144  * afterwards.
145  *
146  * Any errors should either call abort() if they are serious, or
147  * return without harm if not.
148  *
149  * Also remember to call glibc_* for any syscalls that we are
150  * intercepting, otherwise you'll get an infinite loop.
151  */
152 static void
153 br_log (const char *fs, ...)
154 {
155   const char *filename;
156   int fd, len;
157   va_list args;
158   char *msg;
159
160   /* Got the logging environment variable?  Should we cleanse this
161    * variable for security reasons, or is it not a problem because RPM
162    * specfiles can do arbitrary Bad Stuff already? (XXX)
163    */
164   filename = getenv ("AUTO_BUILDREQUIRES_LOGFILE");
165   if (filename == NULL) return;
166
167   fd = glibc_open (filename, O_WRONLY | O_APPEND, 0);
168   if (fd == -1) { perror ("open logfile"); abort (); }
169
170   /* Create the log string. */
171   va_start (args, fs);
172   len = vasprintf (&msg, fs, args);
173   if (len == -1) {
174     perror ("vasprintf");
175     abort ();
176   }
177   va_end (args);
178
179   /* Write it in a single operation.  Should be atomic, right?? (XXX) */
180   if (write (fd, msg, len) != len) {
181     perror ("write");
182     abort ();
183   }
184
185   close (fd);
186   free (msg);
187 }
188
189 static void
190 br_init (void)
191 {
192   void *dl;
193
194   dl = dlopen ("/lib64/libc.so.6", RTLD_LAZY|RTLD_LOCAL);
195   if (dl == NULL)       // Try '/lib/' also
196           dl = dlopen("/lib/libc.so.6", RTLD_LAZY|RTLD_LOCAL);
197   if (dl == NULL) {
198     fprintf (stderr, "%s\n", dlerror ());
199     abort ();
200   }
201   glibc_open = dlsym (dl, "open");
202   glibc_execve = dlsym (dl, "execve");
203 }