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 | |
27 | static void *fb_mem; |
28 | static int fb_stride; |
29 | static int fb_x, fb_y; |
4d045184 |
30 | static int init_done; |
3adc9ccb |
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 | |
adb79840 |
46 | static void fb_text_exit(void) |
47 | { |
48 | if (!init_done) |
49 | return; |
50 | |
51 | host_video_finish(); |
52 | init_done = 0; |
53 | } |
54 | |
4d045184 |
55 | static 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 |
66 | static 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 |
96 | static 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 |
131 | static 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 | |
147 | static 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 | |
187 | out: |
188 | fclose(fi); |
189 | free(phdr); |
190 | return ret; |
191 | } |
192 | |
adb79840 |
193 | static 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 | |
213 | static 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 } |
235 | const 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 | |
245 | static 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 | |
257 | int 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 | |
447 | pass: |
448 | fputs(p, fout); |
449 | } |
450 | |
451 | fclose(fin); |
452 | |
453 | no_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 | |