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 | |
4d045184 |
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 | |
3adc9ccb |
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 | |
4d045184 |
92 | if (!init_done) |
93 | fb_text_init(); |
94 | |
3adc9ccb |
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 | |
3adc9ccb |
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 | |
3adc9ccb |
256 | if (argc < 2) { |
257 | err("usage: %s <script|program> [args]\n", argv[0]); |
258 | return 1; |
259 | } |
260 | |
6ca08393 |
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 | |
3adc9ccb |
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: |
4d045184 |
300 | fprintf(fout, WRAP_APP "%s%s ", root_path, LOADER_STATIC); |
3adc9ccb |
301 | dump_args(fout, argc - 1, &argv[1]); |
302 | fprintf(fout, "\n"); |
303 | goto no_in_script; |
304 | |
305 | case 2: |
4d045184 |
306 | fprintf(fout, WRAP_APP "%s%s \"%s\" ", root_path, LOADER_DYNAMIC, root_path); |
3adc9ccb |
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); |
4d045184 |
404 | fprintf(fout, WRAP_APP "%s%s ", root_path, LOADER_STATIC); |
3adc9ccb |
405 | break; |
406 | |
407 | case 2: |
408 | printf(PFX "prefixing as dynamic: %s", p); |
4d045184 |
409 | fprintf(fout, WRAP_APP "%s%s \"%s\" ", root_path, LOADER_DYNAMIC, root_path); |
3adc9ccb |
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: |
6ca08393 |
424 | #ifdef WIZ |
425 | fprintf(fout, "sync\n"); |
426 | fprintf(fout, "%sginge_prep --cleanup\n", root_path); |
427 | #endif |
3adc9ccb |
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 | |
4d045184 |
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 | } |
3adc9ccb |
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 | |