a03bc38b066fcd39d2978f58adfc5ff6093fff46
[ginge.git] / prep / main.c
1 // vim:shiftwidth=2:expandtab
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <stdarg.h>
6 #include <unistd.h>
7 #include <elf.h>
8 #include <sys/stat.h>
9 #include <ctype.h>
10
11 #include "../common/host_fb.h"
12 #include "../common/cmn.h"
13
14 #define PFX "ging_prep: "
15 #define LOADER_STATIC   "ginge_sloader"
16 #define LOADER_DYNAMIC  "ginge_dyn.sh"
17 #define LAUNCHER        "gp2xmenu"
18
19 #ifdef PND
20 #define WRAP_APP        "op_runfbapp "
21 #else
22 #define WRAP_APP        ""
23 #endif
24
25 #include "font.c"
26
27 static void *fb_mem;
28 static int fb_stride;
29 static int fb_x, fb_y;
30 static int init_done;
31
32 static char *sskip(char *p)
33 {
34   while (p && *p && isspace(*p))
35     p++;
36   return p;
37 }
38
39 static char *cskip(char *p)
40 {
41   while (p && *p && !isspace(*p))
42     p++;
43   return p;
44 }
45
46 static void fb_text_init(void)
47 {
48   int ret = host_video_init(&fb_stride, 1);
49   if (ret == 0)
50     fb_mem = host_video_flip();
51   fb_x = 4;
52   fb_y = 4;
53   init_done = 1;
54 }
55
56 static void fb_syms_out(void *fbi, int x, int y, int dotsz, int stride, const char *text, int count)
57 {
58   int v = -1, n = 0, *p;
59   int i, l;
60   char *fb;
61
62   fb = (char *)fbi + x * dotsz + y * stride;
63
64   for (i = 0; i < count; i++)
65   {
66     for (l = 0; l < 8; l++)
67     {
68       #define pix(fdmask,add) \
69         p = (fontdata8x8[((text[i])*8)+l] & fdmask) ? &v : &n; \
70         memcpy(fb + l*stride + add*dotsz, p, dotsz)
71       pix(0x80,  0);
72       pix(0x40,  1);
73       pix(0x20,  2);
74       pix(0x10,  3);
75       pix(0x08,  4);
76       pix(0x04,  5);
77       pix(0x02,  6);
78       pix(0x01,  7);
79       #undef pix
80     }
81     fb += dotsz * 8;
82   }
83 }
84
85 // FIXME: y overrun
86 static void fb_text_out(char *text)
87 {
88   int dotsz = 2, w = 320; // hardcoded for now
89   char *p, *pe;
90   int l;
91
92   if (!init_done)
93     fb_text_init();
94
95   if (fb_mem == NULL)
96     return;
97
98   p = text;
99   while (*p) {
100     for (; *p && isspace(*p); p++) {
101       if (*p == '\n' || fb_x + dotsz * 8 > w) {
102         fb_x = 4;
103         fb_y += 8;
104       }
105       if (*p >= 0x20)
106         fb_x += 8;
107     }
108
109     pe = cskip(p);
110     l = pe - p;
111     if (fb_x + 8 * l > w) {
112       fb_x = 4;
113       fb_y += 8;
114     }
115     fb_syms_out(fb_mem, fb_x, fb_y, dotsz, fb_stride, p, l);
116     fb_x += 8 * l;
117     p = pe;
118   }
119 }
120
121 static void fbprintf(int is_err, const char *format, ...)
122 {
123   va_list ap;
124   char buff[512];
125
126   va_start(ap, format);
127   vsnprintf(buff, sizeof(buff), format, ap);
128   va_end(ap);
129   fputs(buff, is_err ? stderr : stdout);
130
131   fb_text_out(buff);
132 }
133
134 #define msg(fmt, ...) fbprintf(0, fmt, #__VA_ARGS__)
135 #define err(fmt, ...) fbprintf(1, fmt, #__VA_ARGS__)
136
137 static int id_elf(const char *fname)
138 {
139   Elf32_Ehdr hdr;
140   Elf32_Phdr *phdr = NULL;
141   FILE *fi;
142   int i, ret = 0;
143
144   fi = fopen(fname, "rb");
145   if (fi == NULL) {
146     err("open %s: ", fname);
147     perror("");
148     return -1;
149   }
150
151   if (fread(&hdr, 1, sizeof(hdr), fi) != sizeof(hdr))
152     goto out;
153
154   if (memcmp(hdr.e_ident, ELFMAG "\x01\x01", SELFMAG + 2) != 0)
155     goto out;
156
157   if (hdr.e_phentsize != sizeof(Elf32_Phdr) || hdr.e_phnum == 0)
158     goto out;
159
160   phdr = malloc(hdr.e_phnum * hdr.e_phentsize);
161   if (phdr == NULL)
162     goto out;
163
164   if (fread(phdr, hdr.e_phentsize, hdr.e_phnum, fi) != hdr.e_phnum)
165     goto out;
166
167   ret = 1;
168
169   // do what 'file' does - check for PT_INTERP in program headers
170   for (i = 0; i < hdr.e_phnum; i++) {
171     if (phdr[i].p_type == PT_INTERP) {
172       ret = 2;
173       break;
174     }
175   }
176
177 out:
178   fclose(fi);
179   free(phdr);
180   return ret;
181 }
182
183 static void dump_args(FILE *fout, int argc, char * const argv[])
184 {
185   const char *p;
186   int i;
187
188   for (i = 0; i < argc; i++) {
189     if (i != 0)
190       fputc(' ', fout);
191     fputc('"', fout);
192
193     for (p = argv[i]; *p != 0; p++) {
194       if (*p == '"')
195         fputc('\\', fout);
196       fputc(*p, fout);
197     }
198
199     fputc('"', fout);
200   }
201 }
202
203 static char *get_arg(char *d, size_t size, char *p)
204 {
205   char *pe;
206   int len;
207
208   p = sskip(p);
209   pe = cskip(p);
210   len = pe - p;
211
212   if (len > size - 1) {
213     err(PFX "get_arg: buff to small: %d/%d\n", len, size);
214     len = size - 1;
215   }
216   strncpy(d, p, len);
217   d[len] = 0;
218
219   return sskip(pe);
220 }
221
222 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
223
224 #define CB_ENTRY(x) { x, sizeof(x) - 1 }
225 const struct {
226   const char *name;
227   int len;
228 } conv_blacklist[] = {
229   CB_ENTRY("insmod"),
230   CB_ENTRY("modprobe"),
231   CB_ENTRY("umount"),
232   CB_ENTRY("./cpuctrl_tiny"),
233 };
234
235 static int cmd_in_blacklist(char *cmd)
236 {
237   int i;
238
239   cmd = sskip(cmd);
240   for (i = 0; i < ARRAY_SIZE(conv_blacklist); i++)
241     if (strncmp(cmd, conv_blacklist[i].name, conv_blacklist[i].len) == 0)
242       return 1;
243
244   return 0;
245 }
246
247 int main(int argc, char *argv[])
248 {
249   static const char out_script[] = "/tmp/ginge_conv.sh";
250   char root_path[512], cwd[512];
251   int have_cramfs = 0;
252   int rerun_gp2xmenu = 1;
253   FILE *fin, *fout;
254   int ret;
255
256   if (argc < 2) {
257     err("usage: %s <script|program> [args]\n", argv[0]);
258     return 1;
259   }
260
261   if (strcmp(argv[1], "--cleanup") == 0) {
262     // as loader may crash eny time, restore screen for them menu
263     host_video_init(NULL, 0);
264     host_video_finish();
265     return 0;
266   }
267
268   if (getcwd(cwd, sizeof(cwd)) == NULL) {
269     err(PFX "failed to get cwd\n");
270     return 1;
271   }
272
273   ret = make_local_path(root_path, sizeof(root_path), "");
274   if (ret != 0) {
275     err(PFX "failed to generate root path\n");
276     return 1;
277   }
278
279   fout = fopen(out_script, "w");
280   if (fout == NULL) {
281     perror("can't open output script");
282     return 1;
283   }
284
285   fprintf(fout, "#!/bin/sh\n");
286
287   ret = id_elf(argv[1]);
288   if (ret == 1 || ret == 2) {
289     if (cmd_in_blacklist(argv[1])) {
290       fprintf(stderr, "blacklisted: %s\n", argv[1]);
291       goto no_in_script;
292     }
293   }
294
295   switch (ret) {
296   case 0:
297     break;
298
299   case 1:
300     fprintf(fout, WRAP_APP "%s%s ", root_path, LOADER_STATIC);
301     dump_args(fout, argc - 1, &argv[1]);
302     fprintf(fout, "\n");
303     goto no_in_script;
304
305   case 2:
306     fprintf(fout, WRAP_APP "%s%s \"%s\" ", root_path, LOADER_DYNAMIC, root_path);
307     dump_args(fout, argc - 1, &argv[1]);
308     fprintf(fout, "\n");
309     goto no_in_script;
310
311   default:
312     return 1;
313   }
314
315   // assume script
316   fin = fopen(argv[1], "r");
317   if (fin == NULL)
318     return 1;
319
320   while (1) {
321     char buff[512], fname[512], *p, *p2;
322
323     p = fgets(buff, sizeof(buff), fin);
324     if (p == NULL)
325       break;
326     p = sskip(p);
327
328     if (p[0] == '#' && p[1] == '!')
329       continue;
330
331     if (*p == 0) {
332       fputs("\n", fout);
333       continue;
334     }
335
336     // things we are sure we want to pass
337     if (*p == '#' || strncmp(p, "export ", 7) == 0)
338       goto pass;
339
340     // hmh..
341     if (strncmp(p, "exec ", 5) == 0)
342       p = sskip(p + 5);
343
344     // blacklist some stuff
345     if      (strncmp(p, "/sbin/", 6) == 0)
346       p2 = p + 6;
347     else if (strncmp(p, "/bin/", 5) == 0)
348       p2 = p + 5;
349     else
350       p2 = p;
351     if (strncmp(p2, "mount ", 6) == 0) {
352       p2 = sskip(p2 + 6);
353       // cramfs stuff?
354       if (strstr(p2, "cramfs")) {
355         while (*p2 == '-') {
356           // skip option
357           p2 = sskip(cskip(p2));
358           p2 = sskip(cskip(p2));
359         }
360         if (*p2 == 0) {
361           err(PFX "cramfs: missing mount file in \"%s\"?\n", p);
362           continue;
363         }
364         p2 = get_arg(fname, sizeof(fname), p2);
365         if (*p2 == 0) {
366           err(PFX "cramfs: missing mount point in \"%s\"?\n", p);
367           continue;
368         }
369         get_arg(buff, sizeof(buff), p2);
370         fprintf(fout, "if [ `ls %s | wc -l` -eq 0 ]; then\n", buff);
371         fprintf(fout, "  rmdir \"%s\"\n", buff); // cramfsck doesn't want it
372         fprintf(fout, "  %stools/cramfsck -x \"%s\" \"%s\"\n", root_path, buff, fname);
373         fprintf(fout, "fi\n");
374         have_cramfs = 1;
375       }
376       continue;
377     }
378     if (cmd_in_blacklist(p2))
379       continue;
380
381     // cd?
382     if (strncmp(p, "cd ", 3) == 0) {
383       get_arg(fname, sizeof(fname), p + 3);
384       if (strncmp(fname, "/usr/gp2x", 9) == 0)
385         continue;
386       ret = chdir(fname);
387       if (ret != 0) {
388         err("%s: ", fname);
389         perror("");
390       }
391     }
392
393     // trying to run something from cwd?
394     if ((p[0] == '.' && p[1] == '/') || *p == '/') {
395       get_arg(fname, sizeof(fname), p);
396       p2 = strrchr(fname, '/');
397       if (p2 != NULL && strcmp(p2 + 1, "gp2xmenu") == 0)
398         continue;
399
400       ret = id_elf(fname);
401       switch (ret) {
402       case 1:
403         printf(PFX "prefixing as static: %s", p);
404         fprintf(fout, WRAP_APP "%s%s ", root_path, LOADER_STATIC);
405         break;
406
407       case 2:
408         printf(PFX "prefixing as dynamic: %s", p);
409         fprintf(fout, WRAP_APP "%s%s \"%s\" ", root_path, LOADER_DYNAMIC, root_path);
410         break;
411
412       default:
413         break;
414       }
415     }
416
417 pass:
418     fputs(p, fout);
419   }
420
421   fclose(fin);
422
423 no_in_script:
424 #ifdef WIZ
425   fprintf(fout, "sync\n");
426   fprintf(fout, "%sginge_prep --cleanup\n", root_path);
427 #endif
428   if (rerun_gp2xmenu) {
429     fprintf(fout, "cd %s\n", root_path);
430     fprintf(fout, "exec %s%s\n", root_path, LAUNCHER);
431   }
432
433   fclose(fout);
434
435   //msg("starting script..\n");
436   if (have_cramfs) {
437     msg("\nsome files need to be unpacked, this may tike a few minutes.\n");
438 #ifdef PND
439     msg("Please wait at least while SD LED is active.\n");
440 #endif
441   }
442   system("echo ---; cat /tmp/ginge_conv.sh; echo ---");
443   chmod(out_script, S_IRWXU|S_IRWXG|S_IRWXO);
444   chdir(cwd);
445   execlp(out_script, out_script, NULL);
446   perror("run out_script");
447
448   return 1;
449 }
450