auto-buildrequires-preload: Use libc.so name from GNU headers
[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 #include <gnu/lib-names.h>
40
41 #define ALIAS(ret,syscall,params,brfunc)                                \
42   extern ret syscall params __attribute__((alias (brfunc)))
43
44 static char *br_path (const char *path);
45 static void br_init (void) __attribute__((constructor));
46 static void br_log (const char *fs, ...) __attribute__((format (printf,1,2)));
47
48 /* These are the real glibc symbols, initialized in br_init. */
49 static int (*glibc_open) (const char *pathname, int flags, mode_t mode);
50 static int (*glibc_execve) (const char *filename, char *const argv[],
51                             char *const envp[]);
52
53 /* Canonicalize a relative or absolute path.  If that isn't possible,
54  * return an absolute path.
55  *
56  * The caller must free the result.
57  */
58 static char *
59 br_path (const char *path)
60 {
61   char *dir, *cat, *rp;
62   int len;
63
64   if (path == NULL) return NULL;
65
66   if (path[0] == '/') {
67     rp = realpath (path, NULL);
68     if (!rp) rp = strdup (path);
69     return rp;
70   }
71
72   dir = get_current_dir_name ();
73   if (dir == NULL) return NULL;
74
75   len = strlen (dir) + 1 + strlen (path) + 1;
76   cat = malloc (len);
77   if (cat == NULL) {
78     perror ("malloc");
79     abort ();
80   }
81
82   strcpy (cat, dir);
83   strcat (cat, "/");
84   strcat (cat, path);
85
86   rp = realpath (cat, NULL);
87   free (dir);
88   if (!rp) rp = cat; else free (cat);
89   return rp;
90 }
91
92 int
93 abr_open (const char *pathname, int flags, mode_t mode)
94 {
95   int fd;
96   char *rp;
97
98   rp = br_path (pathname);
99   if (rp)
100     br_log ("open %s\n", rp);
101   else
102     perror (pathname);
103   free (rp);
104
105   fd = glibc_open (pathname, flags, mode);
106   return fd;
107 }
108
109 ALIAS (int, open, (const char *, int, ...), "abr_open");
110
111 int
112 abr_execve (const char *filename, char *const argv[], char *const envp[])
113 {
114   int r;
115   char *rp;
116
117   rp = br_path (filename);
118   if (rp)
119     br_log ("execve %s\n", rp);
120   else
121     perror (filename);
122   free (rp);
123
124   r = glibc_execve (filename, argv, envp);
125   return r;
126 }
127
128 ALIAS (int, execve, (const char *filename, char *const argv[],
129                      char *const envp[]), "abr_execve");
130
131 /* Other syscalls to consider:
132  * access
133  * statfs
134  * stat, lstat
135  * chdir
136  * mkdir
137  * unlink
138  * readlink
139  * 'connect' params can contain a path in some rare circumstances.
140  */
141
142 /* This is the logging function.  We have to be very cautious in this
143  * function and not disturb any part of the process (so far as that is
144  * possible).  eg. When we open a file descriptor we must close it
145  * afterwards.
146  *
147  * Any errors should either call abort() if they are serious, or
148  * return without harm if not.
149  *
150  * Also remember to call glibc_* for any syscalls that we are
151  * intercepting, otherwise you'll get an infinite loop.
152  */
153 static void
154 br_log (const char *fs, ...)
155 {
156   const char *filename;
157   int fd, len;
158   va_list args;
159   char *msg;
160
161   /* Got the logging environment variable?  Should we cleanse this
162    * variable for security reasons, or is it not a problem because RPM
163    * specfiles can do arbitrary Bad Stuff already? (XXX)
164    */
165   filename = getenv ("AUTO_BUILDREQUIRES_LOGFILE");
166   if (filename == NULL) return;
167
168   fd = glibc_open (filename, O_WRONLY | O_APPEND, 0);
169   if (fd == -1) { perror ("open logfile"); abort (); }
170
171   /* Create the log string. */
172   va_start (args, fs);
173   len = vasprintf (&msg, fs, args);
174   if (len == -1) {
175     perror ("vasprintf");
176     abort ();
177   }
178   va_end (args);
179
180   /* Write it in a single operation.  Should be atomic, right?? (XXX) */
181   if (write (fd, msg, len) != len) {
182     perror ("write");
183     abort ();
184   }
185
186   close (fd);
187   free (msg);
188 }
189
190 static void
191 br_init (void)
192 {
193   void *dl;
194
195   dl = dlopen ("/lib64/" LIBC_SO, RTLD_LAZY|RTLD_LOCAL);
196   if (dl == NULL)       // Try '/lib/' also
197           dl = dlopen("/lib/" LIBC_SO, RTLD_LAZY|RTLD_LOCAL);
198   if (dl == NULL) {
199     fprintf (stderr, "%s\n", dlerror ());
200     abort ();
201   }
202   glibc_open = dlsym (dl, "open");
203   glibc_execve = dlsym (dl, "execve");
204 }