- support tilde in rpm version comparison
[platform/upstream/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  *      James Perkins <james.perkins@linuxfoundation.org>
23  */
24
25 #include <sys/mount.h>
26 #include <fcntl.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <stdio.h>
31
32 /* to enable debugging, compile with -DDEBUG */
33 #ifdef DEBUG
34 #define DBG(x)          do { x; } while(0)
35 #else
36 #define DBG(x)
37 #endif
38
39 /* function return codes */
40 enum okfail { FAIL=0, OK=1 };
41
42 /* qemu registration fields, see kernel/Documentation/binfmt_misc.txt */
43 enum fields { ignore=0, name, type, offset, magic, mask, interpreter, flags };
44 const char * const fieldnames[] = {
45         "ignore", "name", "type", "offset",
46         "magic", "mask", "interpreter", "flags"
47 };
48 const int n_fields = 8;
49
50 /* files in useful places */
51 #define SYSFS_BINFMT_MISC       "/proc/sys/fs/binfmt_misc"
52 #define SYSFS_BINFMT_MISC_REG   "/proc/sys/fs/binfmt_misc/register"
53 #define SYSFS_BINFMT_MISC_STAT  "/proc/sys/fs/binfmt_misc/status"
54
55 /* /usr/lib/build/x paths are copied to /.build inside a virtual machine */
56 #define BINFMT_REGF_0           "/.build/qemu-reg"
57 #define BINFMT_REGF_1           "/usr/lib/build/qemu-reg"
58 #define BUILD                   "/.build/build"
59
60 /* useful constant arrays */
61 static char *rx_files[] = { "/proc", "/proc/sys", "/proc/sys/fs",
62          SYSFS_BINFMT_MISC, NULL };
63 static char *w_files[] = { SYSFS_BINFMT_MISC_REG, NULL };
64
65 static char* const args[] = { BUILD, NULL };
66
67 /* test access modes for files, return OK or FAIL */
68 enum okfail test_access_files(char *files[], int mode, const char *errstr)
69 {
70         int i;
71
72         for (i = 0; files[i] != NULL; i++) {
73                 if (access(files[i], mode) != 0) {
74                         fprintf(stderr, "%s: %s: fails test\n",
75                                 files[i], errstr);
76                         return FAIL;
77                 }
78         }
79
80         return OK;
81 }
82
83 /* find a string in the given file, return OK or FAIL */
84 enum okfail strfile(const char *filename, const char *string)
85 {
86         char buf[BUFSIZ];
87         FILE *fp;
88         enum okfail found = FAIL;
89
90         fp = fopen(filename, "r");
91         if (fp == NULL)
92         {
93                 perror(filename);
94                 return FAIL;
95         }
96         while (fgets(buf, sizeof(buf), fp) != NULL)
97         {
98                 if (strcmp(buf, string) == 0) {
99                         found = OK;
100                         break;
101                 }
102
103         }
104         (void)fclose(fp);
105
106         return found;
107 }
108
109 /* write the file with given string, return OK or FAIL */
110 enum okfail write_file_string(const char *filename, const char *string)
111 {
112         int fd;
113
114         if ((fd = open(filename, O_WRONLY)) == -1)
115         {
116                 perror(filename);
117                 return FAIL;
118         }
119
120         if (write(fd, string, strlen(string)) == -1)
121         {
122                 perror("write");
123                 fprintf(stderr, "%s: write failed\n", filename);
124                 close(fd);
125                 return FAIL;
126         }
127
128         close(fd);
129         return OK;
130 }
131
132 #ifdef DEBUG
133 /* dump contents of the file to stderr, return OK or FAIL */
134 enum okfail dump_file(char *path)
135 {
136         FILE *fp;
137         char buf[BUFSIZ];
138
139         fp = fopen(path, "r");
140         if (fp == NULL) {
141                 perror(path);
142                 return FAIL;
143         }
144
145         while (fgets(buf, sizeof(buf), fp) != NULL)
146         {
147                 fputs(buf, stderr);
148         }
149
150         fclose(fp);
151         return OK;
152 }
153 #endif /* DEBUG */
154
155 /* parse datafile and register (to regfile) all binary formats found */
156 enum okfail binfmt_register(char *datafile, char *regfile)
157 {
158         char buf[BUFSIZ];
159         FILE *fp;
160         int line;
161
162         fp = fopen(datafile, "r");
163         if (fp == NULL)
164         {
165                 perror(datafile);
166                 return FAIL;
167         }
168
169         for (line = 1; fgets(buf, sizeof(buf), fp) != NULL; line++)
170         {
171                 char tokens[BUFSIZ];
172                 char *s = tokens;
173                 char *f[n_fields];      /* field content pointers */
174                 int n;                  /* current field */
175                 char path[BUFSIZ];
176
177                 if (buf[0] != ':')      /* non-data input line */
178                 {
179                         goto skip;
180                 }
181
182                 /* copy buf and tokenize :-seperated fields into f[] */
183                 strcpy(tokens, buf);
184                 for (n = 0; s != NULL && n < n_fields; n++)
185                 {
186                         f[n] = strsep(&s, ":");
187                 }
188
189 #ifdef DEBUG
190                 int i;
191                 fprintf(stderr, "DEBUG: line %d, fields %d:\n",  line, n);
192                 for (i = name; i < n; i++)
193                 {
194                         fprintf(stderr, " %s %s\n", fieldnames[i], f[i]);
195                 }
196 #endif /* DEBUG */
197
198                 if (n == n_fields && s != NULL)
199                 {
200                         fprintf(stderr, "%s: line %d: extra fields, ignoring."
201                                 " Content: %s", datafile, line, buf);
202                         goto skip;
203                 }
204
205                 if (n < n_fields)
206                 {
207                         fprintf(stderr, "%s: line %d: missing fields, ignoring."
208                                 " Content: %s", datafile, line, buf);
209                         goto skip;
210                 }
211
212
213                 if (access(f[interpreter], X_OK) != 0) {
214                         fprintf(stderr, 
215                                 "%s: line %d: interpreter '%s' not found,"
216                                 " ignoring\n", datafile, line, f[interpreter]);
217                         goto skip;
218                 }
219
220                 if (!write_file_string(regfile, buf)) {
221                         fprintf(stderr, "%s: line %d: write failed."
222                                 " Content: %s\n", datafile, line, buf);
223                         (void)fclose(fp);
224                         return FAIL;
225                 }
226
227                 /* verify registration completed correctly */
228                 snprintf(path, sizeof(path), SYSFS_BINFMT_MISC "/%s", f[name]);
229
230                 if (access(path, R_OK) != 0) {
231                         fprintf(stderr, 
232                                 "%s: line %d: binfmt path not created, content '%s'\n",
233                                 path, line, buf);
234                         (void)fclose(fp);
235                         return FAIL;
236                 }
237
238                 DBG(fprintf(stderr, "dumping: %s\n", path));
239                 DBG(dump_file(path));
240
241 skip:
242                 ;
243         }
244
245
246         (void)fclose(fp);
247
248         return OK;
249 }
250
251 /* set up/verify binfmt FS support, program more binfmts, and launch build */
252 int main(int argc, char* argv[], char* env[])
253 {
254         int retval;
255
256         /* mount proc filesystem if it isn't already */
257         if (mount("proc", "/proc", "proc", MS_MGC_VAL, NULL) == -1) {
258                 if (errno != EBUSY) {
259                         perror("mount: /proc");
260                         exit(1);
261                 }
262         }
263
264         /* try to load binfmt module if present, no big deal if it fails */
265         if ((retval = system("/sbin/modprobe binfmt_misc")) != 0) {
266                 DBG(fprintf(stderr, "modprobe binfmt_misc exit code %d\n",
267                         retval));
268         }
269
270         /* mount binfmt filesystem */
271         if (mount("binfmt_misc", SYSFS_BINFMT_MISC, "binfmt_misc", MS_MGC_VAL,
272                 NULL) == -1) {
273                 if (errno != EBUSY) {
274                         perror("mount: binfmt_misc, " SYSFS_BINFMT_MISC);
275                 }
276         }
277
278         /* verify all paths resulting from this are OK */
279         if (!test_access_files(rx_files, R_OK|X_OK, "read/search")) {
280                 exit(1);
281         }
282         if (!test_access_files(w_files, W_OK, "write")) {
283                 exit(1);
284         }
285
286         if (!strfile("/proc/filesystems", "nodev\tbinfmt_misc\n")) {
287                 fprintf(stderr,
288                         "/proc/filesystems: binfmt_misc support missing\n");
289                 exit(1);
290         }
291
292         if (!strfile(SYSFS_BINFMT_MISC_STAT, "enabled\n")) {
293                 fprintf(stderr,
294                         "%s: binfmt_misc filesystem support not enabled\n",
295                         SYSFS_BINFMT_MISC_STAT);
296                 exit(1);
297         }
298
299         /* setup all done, do the registration */
300         if (!binfmt_register(BINFMT_REGF_0, SYSFS_BINFMT_MISC_REG)) {
301                 fprintf(stderr, "%s: failed. Trying alternate binfmt file\n",
302                         BINFMT_REGF_0);
303                 if (!binfmt_register(BINFMT_REGF_1, SYSFS_BINFMT_MISC_REG)) {
304                         fprintf(stderr, "%s: binfmt registration failed\n",
305                                 BINFMT_REGF_1);
306                         exit(1);
307                 }
308         }
309
310         /* if we are the init process, start build */
311         if (getpid() == 1)
312         {
313                 if (access(BUILD, F_OK) != 0) {
314                         fprintf(stderr, "%s: build executable missing\n",
315                                 BUILD);
316                         exit(1);
317                 }
318                 if (access(BUILD, X_OK) != 0) {
319                         fprintf(stderr, "%s: not executable\n", BUILD);
320                         exit(1);
321                 }
322                 execve(BUILD, args, env);
323                 perror("execve");
324                 exit(1);
325         }
326
327         /* success! */
328         exit(0);
329 }