sync with latest warm
[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
4d045184 46static 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 56static 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
86static 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 121static 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
137static 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
177out:
178 fclose(fi);
179 free(phdr);
180 return ret;
181}
182
183static 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
203static 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 }
225const 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
235static 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
247int 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
261 if (getcwd(cwd, sizeof(cwd)) == NULL) {
262 err(PFX "failed to get cwd\n");
263 return 1;
264 }
265
266 ret = make_local_path(root_path, sizeof(root_path), "");
267 if (ret != 0) {
268 err(PFX "failed to generate root path\n");
269 return 1;
270 }
271
272 fout = fopen(out_script, "w");
273 if (fout == NULL) {
274 perror("can't open output script");
275 return 1;
276 }
277
278 fprintf(fout, "#!/bin/sh\n");
279
280 ret = id_elf(argv[1]);
281 if (ret == 1 || ret == 2) {
282 if (cmd_in_blacklist(argv[1])) {
283 fprintf(stderr, "blacklisted: %s\n", argv[1]);
284 goto no_in_script;
285 }
286 }
287
288 switch (ret) {
289 case 0:
290 break;
291
292 case 1:
4d045184 293 fprintf(fout, WRAP_APP "%s%s ", root_path, LOADER_STATIC);
3adc9ccb 294 dump_args(fout, argc - 1, &argv[1]);
295 fprintf(fout, "\n");
296 goto no_in_script;
297
298 case 2:
4d045184 299 fprintf(fout, WRAP_APP "%s%s \"%s\" ", root_path, LOADER_DYNAMIC, root_path);
3adc9ccb 300 dump_args(fout, argc - 1, &argv[1]);
301 fprintf(fout, "\n");
302 goto no_in_script;
303
304 default:
305 return 1;
306 }
307
308 // assume script
309 fin = fopen(argv[1], "r");
310 if (fin == NULL)
311 return 1;
312
313 while (1) {
314 char buff[512], fname[512], *p, *p2;
315
316 p = fgets(buff, sizeof(buff), fin);
317 if (p == NULL)
318 break;
319 p = sskip(p);
320
321 if (p[0] == '#' && p[1] == '!')
322 continue;
323
324 if (*p == 0) {
325 fputs("\n", fout);
326 continue;
327 }
328
329 // things we are sure we want to pass
330 if (*p == '#' || strncmp(p, "export ", 7) == 0)
331 goto pass;
332
333 // hmh..
334 if (strncmp(p, "exec ", 5) == 0)
335 p = sskip(p + 5);
336
337 // blacklist some stuff
338 if (strncmp(p, "/sbin/", 6) == 0)
339 p2 = p + 6;
340 else if (strncmp(p, "/bin/", 5) == 0)
341 p2 = p + 5;
342 else
343 p2 = p;
344 if (strncmp(p2, "mount ", 6) == 0) {
345 p2 = sskip(p2 + 6);
346 // cramfs stuff?
347 if (strstr(p2, "cramfs")) {
348 while (*p2 == '-') {
349 // skip option
350 p2 = sskip(cskip(p2));
351 p2 = sskip(cskip(p2));
352 }
353 if (*p2 == 0) {
354 err(PFX "cramfs: missing mount file in \"%s\"?\n", p);
355 continue;
356 }
357 p2 = get_arg(fname, sizeof(fname), p2);
358 if (*p2 == 0) {
359 err(PFX "cramfs: missing mount point in \"%s\"?\n", p);
360 continue;
361 }
362 get_arg(buff, sizeof(buff), p2);
363 fprintf(fout, "if [ `ls %s | wc -l` -eq 0 ]; then\n", buff);
364 fprintf(fout, " rmdir \"%s\"\n", buff); // cramfsck doesn't want it
365 fprintf(fout, " %stools/cramfsck -x \"%s\" \"%s\"\n", root_path, buff, fname);
366 fprintf(fout, "fi\n");
367 have_cramfs = 1;
368 }
369 continue;
370 }
371 if (cmd_in_blacklist(p2))
372 continue;
373
374 // cd?
375 if (strncmp(p, "cd ", 3) == 0) {
376 get_arg(fname, sizeof(fname), p + 3);
377 if (strncmp(fname, "/usr/gp2x", 9) == 0)
378 continue;
379 ret = chdir(fname);
380 if (ret != 0) {
381 err("%s: ", fname);
382 perror("");
383 }
384 }
385
386 // trying to run something from cwd?
387 if ((p[0] == '.' && p[1] == '/') || *p == '/') {
388 get_arg(fname, sizeof(fname), p);
389 p2 = strrchr(fname, '/');
390 if (p2 != NULL && strcmp(p2 + 1, "gp2xmenu") == 0)
391 continue;
392
393 ret = id_elf(fname);
394 switch (ret) {
395 case 1:
396 printf(PFX "prefixing as static: %s", p);
4d045184 397 fprintf(fout, WRAP_APP "%s%s ", root_path, LOADER_STATIC);
3adc9ccb 398 break;
399
400 case 2:
401 printf(PFX "prefixing as dynamic: %s", p);
4d045184 402 fprintf(fout, WRAP_APP "%s%s \"%s\" ", root_path, LOADER_DYNAMIC, root_path);
3adc9ccb 403 break;
404
405 default:
406 break;
407 }
408 }
409
410pass:
411 fputs(p, fout);
412 }
413
414 fclose(fin);
415
416no_in_script:
417 if (rerun_gp2xmenu) {
418 fprintf(fout, "cd %s\n", root_path);
419 fprintf(fout, "exec %s%s\n", root_path, LAUNCHER);
420 }
421
422 fclose(fout);
423
4d045184 424 //msg("starting script..\n");
425 if (have_cramfs) {
426 msg("\nsome files need to be unpacked, this may tike a few minutes.\n");
427#ifdef PND
428 msg("Please wait at least while SD LED is active.\n");
429#endif
430 }
3adc9ccb 431 system("echo ---; cat /tmp/ginge_conv.sh; echo ---");
432 chmod(out_script, S_IRWXU|S_IRWXG|S_IRWXO);
433 chdir(cwd);
434 execlp(out_script, out_script, NULL);
435 perror("run out_script");
436
437 return 1;
438}
439