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