Merge "Support BuildFlags: nocumulaterpms" into devel
[tools/build.git] / initvm.c
1 /*
2  * NAME
3  *      initvm - init for qemu, setup binfmt_misc launch build
4  *      
5  * SYNOPSIS
6  *      initvm
7  *
8  * DESCRIPTION
9  *      This is the kernel init script for virtual machines which will
10  *      be running executables for an embedded (non-native)
11  *      architecture. It registers binfmt_misc handlers for qemu and
12  *      executes the build script, and tests many assumptions.
13  *
14  * FILES
15  *      /.build/qemu-reg
16  *              text file with lines to stuff into the binfmt_misc
17  *              filesystem registration file
18  *      /.build/build
19  *              build script to execute once binfmts are set up
20  *
21  * AUTHOR
22  *      Copyright (c) 2012 James Perkins <james.perkins@linuxfoundation.org>
23  *                         Adrian Schroeter <adrian@suse.de>
24  *
25  * This program is free software; you can redistribute it and/or modify
26  * it under the terms of the GNU General Public License version 2 or 3 as
27  * published by the Free Software Foundation.
28  *
29  * This program is distributed in the hope that it will be useful,
30  * but WITHOUT ANY WARRANTY; without even the implied warranty of
31  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32  * GNU General Public License for more details.
33  *
34  * You should have received a copy of the GNU General Public License
35  * along with this program (see the file COPYING); if not, write to the
36  * Free Software Foundation, Inc.,
37  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
38  */
39
40 #include <sys/mount.h>
41 #include <sys/stat.h>
42 #include <sys/utsname.h>
43 #include <fcntl.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <errno.h>
47 #include <stdio.h>
48 #include <unistd.h>
49
50 /* to enable debugging, compile with -DDEBUG */
51 #ifdef DEBUG
52 #define DBG(x)          do { x; } while(0)
53 #else
54 #define DBG(x)
55 #endif
56
57 /* function return codes */
58 enum okfail { FAIL=0, OK=1 };
59
60 /* qemu registration fields, see kernel/Documentation/binfmt_misc.txt */
61 enum fields { ignore=0, name, type, offset, magic, mask, interpreter, flags };
62 const char * const fieldnames[] = {
63         "ignore", "name", "type", "offset",
64         "magic", "mask", "interpreter", "flags"
65 };
66 const int n_fields = 8;
67
68 /* files in useful places */
69 #define SYSFS_BINFMT_MISC       "/proc/sys/fs/binfmt_misc"
70 #define SYSFS_BINFMT_MISC_REG   "/proc/sys/fs/binfmt_misc/register"
71 #define SYSFS_BINFMT_MISC_STAT  "/proc/sys/fs/binfmt_misc/status"
72
73 /* /usr/lib/build/x paths are copied to /.build inside a virtual machine */
74 #define BINFMT_REGF_0           "/.build/qemu-reg"
75 #define BINFMT_REGF_1           "/usr/lib/build/qemu-reg"
76 #define BUILD                   "/.build/build"
77
78 /* useful constant arrays */
79 static char *rx_files[] = { "/proc", "/proc/sys", "/proc/sys/fs",
80          SYSFS_BINFMT_MISC, NULL };
81 static char *w_files[] = { SYSFS_BINFMT_MISC_REG, NULL };
82
83 static char* const args[] = { BUILD, NULL };
84
85 /* test access modes for files, return OK or FAIL */
86 enum okfail test_access_files(char *files[], int mode, const char *errstr)
87 {
88         int i;
89
90         for (i = 0; files[i] != NULL; i++) {
91                 if (access(files[i], mode) != 0) {
92                         fprintf(stderr, "%s: %s: fails test\n",
93                                 files[i], errstr);
94                         return FAIL;
95                 }
96         }
97
98         return OK;
99 }
100
101 /* find a string in the given file, return OK or FAIL */
102 enum okfail strfile(const char *filename, const char *string)
103 {
104         char buf[BUFSIZ];
105         FILE *fp;
106         enum okfail found = FAIL;
107
108         fp = fopen(filename, "r");
109         if (fp == NULL)
110         {
111                 perror(filename);
112                 return FAIL;
113         }
114         while (fgets(buf, sizeof(buf), fp) != NULL)
115         {
116                 if (strcmp(buf, string) == 0) {
117                         found = OK;
118                         break;
119                 }
120
121         }
122         (void)fclose(fp);
123
124         return found;
125 }
126
127 /* write the file with given string, return OK or FAIL */
128 enum okfail write_file_string(const char *filename, const char *string)
129 {
130         int fd;
131
132         if ((fd = open(filename, O_WRONLY)) == -1)
133         {
134                 perror(filename);
135                 return FAIL;
136         }
137
138         if (write(fd, string, strlen(string)) == -1)
139         {
140                 perror("write");
141                 fprintf(stderr, "%s: write failed\n", filename);
142                 close(fd);
143                 return FAIL;
144         }
145
146         close(fd);
147         return OK;
148 }
149
150 #ifdef DEBUG
151 /* dump contents of the file to stderr, return OK or FAIL */
152 enum okfail dump_file(char *path)
153 {
154         FILE *fp;
155         char buf[BUFSIZ];
156
157         fp = fopen(path, "r");
158         if (fp == NULL) {
159                 perror(path);
160                 return FAIL;
161         }
162
163         while (fgets(buf, sizeof(buf), fp) != NULL)
164         {
165                 fputs(buf, stderr);
166         }
167
168         fclose(fp);
169         return OK;
170 }
171 #endif /* DEBUG */
172
173 /* parse datafile and register (to regfile) all binary formats found */
174 enum okfail binfmt_register(char *datafile, char *regfile)
175 {
176         char buf[BUFSIZ];
177         FILE *fp;
178         int line;
179         struct utsname myuname;
180         uname(&myuname);
181
182         fp = fopen(datafile, "r");
183         if (fp == NULL)
184         {
185                 perror(datafile);
186                 return FAIL;
187         }
188
189         for (line = 1; fgets(buf, sizeof(buf), fp) != NULL; line++)
190         {
191                 char tokens[BUFSIZ];
192                 char *s = tokens;
193                 char *blacklist;
194                 char *f[n_fields];      /* field content pointers */
195                 int n;                  /* current field */
196                 char path[BUFSIZ];
197
198                 if (buf[0] != ':')      /* non-data input line */
199                 {
200                         continue;
201                 }
202                 blacklist = strchr(buf, ' ');
203                 if (blacklist) {
204                         int skip = 0;
205                         char *eol;
206
207                         *blacklist = '\0';
208                         blacklist++;
209
210                         eol = strchr(blacklist, '\n');
211                         if (eol)
212                                 *eol = '\0';
213
214                         for (n = 0; blacklist != NULL; n++)
215                         {
216                                 char *bp = strsep(&blacklist, " ");
217                                 if (!strcmp(bp, myuname.machine)) {
218 #ifdef DEBUG
219                                         fprintf(stderr, " skipping on hostarch %s line %s\n", bp, buf);
220 #endif /* DEBUG */
221                                         skip = 1;
222                                         break;
223                                 }
224                         }
225                         if (skip)
226                                 continue;
227                 }
228
229                 /* copy buf and tokenize :-seperated fields into f[] */
230                 strcpy(tokens, buf);
231                 for (n = 0; s != NULL && n < n_fields; n++)
232                 {
233                         f[n] = strsep(&s, ":");
234                 }
235
236 #ifdef DEBUG
237                 int i;
238                 fprintf(stderr, "DEBUG: line %d, fields %d:\n",  line, n);
239                 for (i = name; i < n; i++)
240                 {
241                         fprintf(stderr, " %s %s\n", fieldnames[i], f[i]);
242                 }
243 #endif /* DEBUG */
244
245                 if (n == n_fields && s != NULL)
246                 {
247                         fprintf(stderr, "%s: line %d: extra fields, ignoring."
248                                 " Content: %s", datafile, line, buf);
249                         continue;
250                 }
251
252                 if (n < n_fields)
253                 {
254                         fprintf(stderr, "%s: line %d: missing fields, ignoring."
255                                 " Content: %s", datafile, line, buf);
256                         continue;
257                 }
258
259                 int ret;
260                 /* Is an interpreter for this arch already registered? */
261                 snprintf(path, sizeof(path), SYSFS_BINFMT_MISC "/%s", f[name]);
262                 ret=access(path, F_OK);
263                 if (ret == 0) {
264 #ifdef DEBUG
265                         fprintf(stderr, 
266                                 "interpreter for '%s' already registered, ignoring\n",
267                                 f[name]);
268 #endif /* DEBUG */
269                         continue;
270                 }
271 #ifdef DEBUG
272                 fprintf(stderr, 
273                         "registering interpreter for '%s'...\n",
274                         f[name]);
275 #endif /* DEBUG */
276
277         /* Does the interpreter exists? */
278                 ret=access(f[interpreter], X_OK);
279                 if (ret != 0) {
280                         fprintf(stderr, 
281                                 "%s: line %d: interpreter '%s' not found,"
282                                 " ignoring, return %d\n", datafile, line, f[interpreter], ret);
283                         continue;
284                 }
285
286                 if (!write_file_string(regfile, buf)) {
287                         fprintf(stderr, "%s: line %d: write failed."
288                                 " Content: %s\n", datafile, line, buf);
289                         (void)fclose(fp);
290                         return FAIL;
291                 }
292
293                 /* verify registration completed correctly */
294                 snprintf(path, sizeof(path), SYSFS_BINFMT_MISC "/%s", f[name]);
295
296                 if (access(path, R_OK) != 0) {
297                         fprintf(stderr, 
298                                 "%s: line %d: binfmt path not created, content '%s'\n",
299                                 path, line, buf);
300                         (void)fclose(fp);
301                         return FAIL;
302                 }
303
304                 DBG(fprintf(stderr, "dumping: %s\n", path));
305                 DBG(dump_file(path));
306         }
307
308
309         (void)fclose(fp);
310
311         return OK;
312 }
313
314 /* set up/verify binfmt FS support, program more binfmts, and launch build */
315 int main(int argc, char* argv[], char* env[])
316 {
317         int retval;
318         char buf[BUFSIZ], *build_dir;
319
320         if (system("mount | grep 'proc on /proc type proc' >/dev/null") != 0) {
321                 /* mount proc filesystem if it isn't already. */
322                 if (mount("proc", "/proc", "proc", MS_MGC_VAL, NULL) == -1) {
323                         if (errno != EBUSY) {
324                                 perror("mount: /proc");
325                                 exit(1);
326                         }
327                 }
328         }
329
330         /* need to have binfmt_misc loaded already */
331         if (system("mount | grep 'binfmt_misc on /proc/sys/fs/binfmt_misc' >/dev/null") != 0) {
332                 /* try to load binfmt module if present, no big deal if it fails */
333                 if ((retval = system("/sbin/modprobe binfmt_misc")) != 0) {
334                         DBG(fprintf(stderr, "modprobe binfmt_misc exit code %d\n",
335                                 retval));
336                 }
337
338                 /* mount binfmt filesystem */
339                 if (mount("binfmt_misc", SYSFS_BINFMT_MISC, "binfmt_misc", MS_MGC_VAL,
340                         NULL) == -1) {
341                         if (errno != EBUSY) {
342                                 perror("mount: binfmt_misc, " SYSFS_BINFMT_MISC);
343                         }
344                 }
345         }
346
347         /* verify all paths resulting from this are OK */
348         if (!test_access_files(rx_files, R_OK|X_OK, "read/search")) {
349                 exit(1);
350         }
351         if (!test_access_files(w_files, W_OK, "write")) {
352                 exit(1);
353         }
354
355         if (!strfile("/proc/filesystems", "nodev\tbinfmt_misc\n")) {
356                 fprintf(stderr,
357                         "/proc/filesystems: binfmt_misc support missing\n");
358                 exit(1);
359         }
360
361         if (!strfile(SYSFS_BINFMT_MISC_STAT, "enabled\n")) {
362                 fprintf(stderr,
363                         "%s: binfmt_misc filesystem support not enabled\n",
364                         SYSFS_BINFMT_MISC_STAT);
365                 exit(1);
366         }
367
368         *buf = 0;
369         build_dir = getenv("BUILD_DIR");
370         if (build_dir && strlen(build_dir) < sizeof(buf) - 10)
371                 sprintf(buf, "%s/qemu-reg", build_dir);
372
373         if (!*buf || !binfmt_register(buf, SYSFS_BINFMT_MISC_REG)) {
374                 /* setup all done, do the registration */
375                 if (!binfmt_register(BINFMT_REGF_0, SYSFS_BINFMT_MISC_REG)) {
376                         fprintf(stderr, "%s: failed. Trying alternate binfmt file\n",
377                                 BINFMT_REGF_0);
378                         if (!binfmt_register(BINFMT_REGF_1, SYSFS_BINFMT_MISC_REG)) {
379                                 fprintf(stderr, "%s: binfmt registration failed\n",
380                                         BINFMT_REGF_1);
381                                 exit(1);
382                         }
383                 }
384         }
385
386         /* if we are the init process, start build */
387         if (getpid() == 1)
388         {
389                 if (access(BUILD, F_OK) != 0) {
390                         fprintf(stderr, "%s: build executable missing\n",
391                                 BUILD);
392                         exit(1);
393                 }
394                 if (access(BUILD, X_OK) != 0) {
395                         fprintf(stderr, "%s: not executable\n", BUILD);
396                         exit(1);
397                 }
398                 execve(BUILD, args, env);
399                 perror("execve of "BUILD);
400                 exit(1);
401         }
402
403         /* success! */
404         exit(0);
405 }