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