tonarchy

tonarchy

https://git.tonybtw.com/tonarchy.git git://git.tonybtw.com/tonarchy.git
15,768 bytes raw
1
#include "build_iso.h"
2
#include <stdarg.h>
3
4
static FILE *log_file = NULL;
5
6
void logger_init(const char *log_path) {
7
    log_file = fopen(log_path, "a");
8
    if (log_file) {
9
        time_t now = time(NULL);
10
        char *timestamp = ctime(&now);
11
        timestamp[strlen(timestamp) - 1] = '\0';
12
        fprintf(log_file, "\n=== Tonarchy ISO Build Log - %s ===\n", timestamp);
13
        fflush(log_file);
14
    }
15
}
16
17
void logger_close(void) {
18
    if (log_file) {
19
        fclose(log_file);
20
        log_file = NULL;
21
    }
22
}
23
24
void log_info(const char *fmt, ...) {
25
    va_list args;
26
    va_start(args, fmt);
27
    printf("[INFO] ");
28
    vprintf(fmt, args);
29
    printf("\n");
30
    va_end(args);
31
32
    if (log_file) {
33
        va_start(args, fmt);
34
        fprintf(log_file, "[INFO] ");
35
        vfprintf(log_file, fmt, args);
36
        fprintf(log_file, "\n");
37
        fflush(log_file);
38
        va_end(args);
39
    }
40
}
41
42
void log_error(const char *fmt, ...) {
43
    va_list args;
44
    va_start(args, fmt);
45
    fprintf(stderr, "[ERROR] ");
46
    vfprintf(stderr, fmt, args);
47
    fprintf(stderr, "\n");
48
    va_end(args);
49
50
    if (log_file) {
51
        va_start(args, fmt);
52
        fprintf(log_file, "[ERROR] ");
53
        vfprintf(log_file, fmt, args);
54
        fprintf(log_file, "\n");
55
        fflush(log_file);
56
        va_end(args);
57
    }
58
}
59
60
void log_warn(const char *fmt, ...) {
61
    va_list args;
62
    va_start(args, fmt);
63
    fprintf(stderr, "[WARN] ");
64
    vfprintf(stderr, fmt, args);
65
    fprintf(stderr, "\n");
66
    va_end(args);
67
68
    if (log_file) {
69
        va_start(args, fmt);
70
        fprintf(log_file, "[WARN] ");
71
        vfprintf(log_file, fmt, args);
72
        fprintf(log_file, "\n");
73
        fflush(log_file);
74
        va_end(args);
75
    }
76
}
77
78
int run_command(const char *cmd) {
79
    log_info("Running: %s", cmd);
80
    int ret = system(cmd);
81
    if (ret != 0) {
82
        log_error("Command failed with code %d: %s", ret, cmd);
83
        return 0;
84
    }
85
    return 1;
86
}
87
88
int run_command_in_container(const char *cmd, const Build_Config *config) {
89
    char container_cmd[CMD_MAX_LEN];
90
91
    if (config->container_type == CONTAINER_DISTROBOX) {
92
        snprintf(container_cmd, sizeof(container_cmd),
93
                 "distrobox enter %s -- sh -c '%s'",
94
                 config->distrobox_name, cmd);
95
    } else if (config->container_type == CONTAINER_PODMAN) {
96
        snprintf(container_cmd, sizeof(container_cmd),
97
                 "podman run --rm --privileged "
98
                 "-v '%s:/src' "
99
                 "-v '%s:/profile' "
100
                 "-v '%s:/out' "
101
                 "-v '%s:/work' "
102
                 "docker.io/archlinux:latest sh -c '%s'",
103
                 config->tonarchy_src,
104
                 config->iso_profile,
105
                 config->out_dir,
106
                 config->work_dir,
107
                 cmd);
108
    } else {
109
        return run_command(cmd);
110
    }
111
112
    return run_command(container_cmd);
113
}
114
115
int create_directory(const char *path, mode_t mode) {
116
    struct stat st;
117
    if (stat(path, &st) == 0) {
118
        return 1;
119
    }
120
121
    if (mkdir(path, mode) != 0) {
122
        log_error("Failed to create directory: %s", path);
123
        return 0;
124
    }
125
    return 1;
126
}
127
128
int detect_container_runtime(void) {
129
    if (system("command -v podman >/dev/null 2>&1") == 0) {
130
        return 1;
131
    }
132
    if (system("command -v distrobox >/dev/null 2>&1") == 0) {
133
        return 1;
134
    }
135
    return 0;
136
}
137
138
int check_distrobox_exists(const char *name) {
139
    char cmd[CMD_MAX_LEN];
140
    snprintf(cmd, sizeof(cmd), "distrobox list | grep -q '%s'", name);
141
    return system(cmd) == 0;
142
}
143
144
int build_tonarchy_static(const Build_Config *config) {
145
    log_info("Building tonarchy static binary...");
146
147
    char cmd[CMD_MAX_LEN];
148
149
    if (config->use_container && config->container_type == CONTAINER_PODMAN) {
150
        log_info("Building static binary in podman container...");
151
        snprintf(cmd, sizeof(cmd),
152
                 "sudo podman run --rm "
153
                 "-v '%s:/src' "
154
                 "docker.io/archlinux:latest "
155
                 "sh -c 'pacman -Sy --noconfirm musl gcc && "
156
                 "cd /src && rm -f tonarchy tonarchy-static && "
157
                 "musl-gcc -std=c23 -Wall -Wextra -O2 -static src/tonarchy.c -o tonarchy-static'",
158
                 config->tonarchy_src);
159
        if (!run_command(cmd)) {
160
            log_error("Failed to build tonarchy-static in podman");
161
            return 0;
162
        }
163
    } else if (config->use_container && config->container_type == CONTAINER_DISTROBOX) {
164
        snprintf(cmd, sizeof(cmd),
165
                 "sudo pacman -S --noconfirm --needed musl && "
166
                 "cd '%s' && make clean && make static CC=musl-gcc",
167
                 config->tonarchy_src);
168
        if (!run_command_in_container(cmd, config)) {
169
            log_error("Failed to build tonarchy-static in distrobox");
170
            return 0;
171
        }
172
    } else {
173
        snprintf(cmd, sizeof(cmd),
174
                 "cd '%s' && make clean && make static CC=musl-gcc",
175
                 config->tonarchy_src);
176
        if (!run_command(cmd)) {
177
            log_error("Failed to build tonarchy-static");
178
            return 0;
179
        }
180
    }
181
182
    char binary_path[PATH_MAX_LEN];
183
    snprintf(binary_path, sizeof(binary_path), "%s/tonarchy-static", config->tonarchy_src);
184
185
    struct stat st;
186
    if (stat(binary_path, &st) != 0) {
187
        log_error("tonarchy-static binary not found at %s", binary_path);
188
        return 0;
189
    }
190
191
    log_info("Built tonarchy-static successfully");
192
    return 1;
193
}
194
195
int clean_airootfs(const Build_Config *config) {
196
    log_info("Cleaning airootfs...");
197
198
    char cmd[CMD_MAX_LEN];
199
200
    snprintf(cmd, sizeof(cmd), "sudo rm -rf '%s/airootfs/usr'", config->iso_profile);
201
    if (!run_command(cmd)) {
202
        log_warn("Failed to clean airootfs/usr");
203
    }
204
205
    snprintf(cmd, sizeof(cmd), "sudo rm -rf '%s/airootfs/root/tonarchy'", config->iso_profile);
206
    if (!run_command(cmd)) {
207
        log_warn("Failed to clean airootfs/root/tonarchy");
208
    }
209
210
    return 1;
211
}
212
213
int clean_work_dir(const Build_Config *config) {
214
    log_info("Cleaning work directory...");
215
216
    char cmd[CMD_MAX_LEN];
217
218
    snprintf(cmd, sizeof(cmd), "sudo umount -R '%s' 2>/dev/null || true", config->work_dir);
219
    run_command(cmd);
220
221
    snprintf(cmd, sizeof(cmd), "sudo rm -rf '%s'", config->work_dir);
222
    if (!run_command(cmd)) {
223
        log_error("Failed to remove work directory: %s", config->work_dir);
224
        return 0;
225
    }
226
227
    sync();
228
    sleep(1);
229
230
    return 1;
231
}
232
233
int prepare_airootfs(const Build_Config *config) {
234
    log_info("Preparing airootfs...");
235
236
    char cmd[CMD_MAX_LEN];
237
    char src_path[PATH_MAX_LEN];
238
    char dest_path[PATH_MAX_LEN];
239
240
    snprintf(cmd, sizeof(cmd), "mkdir -p '%s/airootfs/usr/local/bin'", config->iso_profile);
241
    if (!run_command(cmd)) {
242
        log_error("Failed to create airootfs/usr/local/bin");
243
        return 0;
244
    }
245
246
    snprintf(cmd, sizeof(cmd), "mkdir -p '%s/airootfs/usr/share'", config->iso_profile);
247
    if (!run_command(cmd)) {
248
        log_error("Failed to create airootfs/usr/share");
249
        return 0;
250
    }
251
252
    snprintf(src_path, sizeof(src_path), "%s/tonarchy-static", config->tonarchy_src);
253
    snprintf(dest_path, sizeof(dest_path), "%s/airootfs/usr/local/bin/tonarchy", config->iso_profile);
254
    snprintf(cmd, sizeof(cmd), "cp '%s' '%s'", src_path, dest_path);
255
    if (!run_command(cmd)) {
256
        log_error("Failed to copy tonarchy binary");
257
        return 0;
258
    }
259
260
    snprintf(cmd, sizeof(cmd), "chmod 755 '%s'", dest_path);
261
    if (!run_command(cmd)) {
262
        log_error("Failed to set permissions on tonarchy binary");
263
        return 0;
264
    }
265
266
    snprintf(src_path, sizeof(src_path), "%s/assets", config->tonarchy_src);
267
    snprintf(dest_path, sizeof(dest_path), "%s/airootfs/usr/share/tonarchy", config->iso_profile);
268
    snprintf(cmd, sizeof(cmd), "mkdir -p '%s'", dest_path);
269
    if (!run_command(cmd)) {
270
        log_error("Failed to create tonarchy share directory");
271
        return 0;
272
    }
273
    snprintf(cmd, sizeof(cmd), "cp -r '%s'/* '%s'", src_path, dest_path);
274
    if (!run_command(cmd)) {
275
        log_error("Failed to copy tonarchy config files");
276
        return 0;
277
    }
278
279
    snprintf(dest_path, sizeof(dest_path), "%s/airootfs/usr/share/wallpapers", config->iso_profile);
280
    snprintf(cmd, sizeof(cmd), "cp -r '%s/wallpapers' '%s'", src_path, dest_path);
281
    if (!run_command(cmd)) {
282
        log_warn("Failed to copy wallpapers");
283
    }
284
285
    log_info("Setting proper ownership for airootfs...");
286
    snprintf(cmd, sizeof(cmd), "sudo chown -R root:root '%s/airootfs/usr'", config->iso_profile);
287
    if (!run_command(cmd)) {
288
        log_error("Failed to set ownership on airootfs");
289
        return 0;
290
    }
291
292
    return 1;
293
}
294
295
int run_mkarchiso(const Build_Config *config) {
296
    log_info("Building ISO with mkarchiso...");
297
298
    if (!create_directory(config->out_dir, 0755)) {
299
        return 0;
300
    }
301
302
    if (!create_directory(config->work_dir, 0755)) {
303
        return 0;
304
    }
305
306
    char cmd[CMD_MAX_LEN];
307
    snprintf(cmd, sizeof(cmd), "sudo mkarchiso -v -w '%s' -o '%s' '%s'",
308
             config->work_dir, config->out_dir, config->iso_profile);
309
310
    if (!run_command(cmd)) {
311
        log_error("mkarchiso failed");
312
        return 0;
313
    }
314
315
    return 1;
316
}
317
318
int run_mkarchiso_in_container(const Build_Config *config) {
319
    log_info("Building ISO with mkarchiso in container...");
320
321
    if (!create_directory(config->out_dir, 0755)) {
322
        return 0;
323
    }
324
325
    if (!create_directory(config->work_dir, 0755)) {
326
        return 0;
327
    }
328
329
    char cmd[CMD_MAX_LEN];
330
331
    log_info("Setting up container policy...");
332
    run_command("sudo mkdir -p /etc/containers");
333
    run_command("echo '{\"default\":[{\"type\":\"insecureAcceptAnything\"}]}' | sudo tee /etc/containers/policy.json > /dev/null");
334
335
    snprintf(cmd, sizeof(cmd),
336
             "sudo podman run --rm --privileged "
337
             "-v '%s:/profile' "
338
             "-v '%s:/out' "
339
             "-v '%s:/work' "
340
             "docker.io/archlinux:latest "
341
             "sh -c 'pacman -Sy --noconfirm archiso && mkarchiso -v -w /work -o /out /profile'",
342
             config->iso_profile,
343
             config->out_dir,
344
             config->work_dir);
345
346
    if (!run_command(cmd)) {
347
        log_error("mkarchiso in container failed");
348
        return 0;
349
    }
350
351
    return 1;
352
}
353
354
const char *find_latest_iso(const char *out_dir) {
355
    static char iso_path[PATH_MAX_LEN];
356
    char cmd[CMD_MAX_LEN];
357
358
    snprintf(cmd, sizeof(cmd), "ls -t '%s'/*.iso 2>/dev/null | head -n1", out_dir);
359
360
    FILE *fp = popen(cmd, "r");
361
    if (!fp) {
362
        return NULL;
363
    }
364
365
    if (fgets(iso_path, sizeof(iso_path), fp) != NULL) {
366
        iso_path[strcspn(iso_path, "\n")] = 0;
367
        pclose(fp);
368
        return iso_path;
369
    }
370
371
    pclose(fp);
372
    return NULL;
373
}
374
375
static void print_usage(const char *prog_name) {
376
    printf("Usage: %s [OPTIONS]\n", prog_name);
377
    printf("\nOptions:\n");
378
    printf("  --iso-profile PATH    Path to ISO profile directory (default: ./iso)\n");
379
    printf("  --out-dir PATH        Output directory for ISO (default: ./out)\n");
380
    printf("  --container [TYPE]    Build using container (podman or distrobox)\n");
381
    printf("  --distrobox NAME      Distrobox container name (default: arch)\n");
382
    printf("  -h, --help            Show this help message\n");
383
}
384
385
static int parse_args(int argc, char *argv[], Build_Config *config) {
386
    for (int i = 1; i < argc; i++) {
387
        if (strcmp(argv[i], "--iso-profile") == 0 && i + 1 < argc) {
388
            snprintf(config->iso_profile, sizeof(config->iso_profile), "%s", argv[++i]);
389
        } else if (strcmp(argv[i], "--out-dir") == 0 && i + 1 < argc) {
390
            snprintf(config->out_dir, sizeof(config->out_dir), "%s", argv[++i]);
391
        } else if (strcmp(argv[i], "--container") == 0) {
392
            config->use_container = true;
393
            if (i + 1 < argc && argv[i + 1][0] != '-') {
394
                i++;
395
                if (strcmp(argv[i], "podman") == 0) {
396
                    config->container_type = CONTAINER_PODMAN;
397
                } else if (strcmp(argv[i], "distrobox") == 0) {
398
                    config->container_type = CONTAINER_DISTROBOX;
399
                } else {
400
                    log_error("Unknown container type: %s", argv[i]);
401
                    return 0;
402
                }
403
            } else {
404
                config->container_type = CONTAINER_PODMAN;
405
            }
406
        } else if (strcmp(argv[i], "--distrobox") == 0 && i + 1 < argc) {
407
            snprintf(config->distrobox_name, sizeof(config->distrobox_name), "%s", argv[++i]);
408
            config->use_container = true;
409
            config->container_type = CONTAINER_DISTROBOX;
410
        } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
411
            print_usage(argv[0]);
412
            exit(0);
413
        } else {
414
            log_error("Unknown option: %s", argv[i]);
415
            print_usage(argv[0]);
416
            return 0;
417
        }
418
    }
419
    return 1;
420
}
421
422
int main(int argc, char *argv[]) {
423
    logger_init("/tmp/build_iso.log");
424
425
    log_info("Tonarchy ISO Builder starting...");
426
427
    Build_Config config = {
428
        .work_dir = "/tmp/tonarchy_iso_work",
429
        .distrobox_name = "arch",
430
        .container_type = CONTAINER_NONE,
431
        .use_container = false
432
    };
433
434
    if (getcwd(config.tonarchy_src, sizeof(config.tonarchy_src)) == NULL) {
435
        log_error("Failed to get current directory");
436
        return 1;
437
    }
438
439
    snprintf(config.iso_profile, sizeof(config.iso_profile), "%s/iso", config.tonarchy_src);
440
    snprintf(config.out_dir, sizeof(config.out_dir), "%s/out", config.tonarchy_src);
441
442
    if (!parse_args(argc, argv, &config)) {
443
        logger_close();
444
        return 1;
445
    }
446
447
    log_info("Tonarchy source: %s", config.tonarchy_src);
448
    log_info("ISO profile: %s", config.iso_profile);
449
    log_info("Work directory: %s", config.work_dir);
450
    log_info("Output directory: %s", config.out_dir);
451
452
    if (config.use_container) {
453
        log_info("Container mode: %s",
454
                 config.container_type == CONTAINER_PODMAN ? "podman" : "distrobox");
455
        if (config.container_type == CONTAINER_DISTROBOX) {
456
            log_info("Distrobox name: %s", config.distrobox_name);
457
        }
458
    }
459
460
    if (!build_tonarchy_static(&config)) {
461
        log_error("Build failed");
462
        logger_close();
463
        return 1;
464
    }
465
466
    if (!clean_airootfs(&config)) {
467
        log_error("Failed to clean airootfs");
468
        logger_close();
469
        return 1;
470
    }
471
472
    if (!clean_work_dir(&config)) {
473
        log_error("Failed to clean work directory");
474
        logger_close();
475
        return 1;
476
    }
477
478
    if (!prepare_airootfs(&config)) {
479
        log_error("Failed to prepare airootfs");
480
        logger_close();
481
        return 1;
482
    }
483
484
    int build_result;
485
    if (config.use_container && config.container_type == CONTAINER_PODMAN) {
486
        build_result = run_mkarchiso_in_container(&config);
487
    } else {
488
        build_result = run_mkarchiso(&config);
489
    }
490
491
    if (!build_result) {
492
        log_error("Failed to build ISO");
493
        logger_close();
494
        return 1;
495
    }
496
497
    if (!clean_work_dir(&config)) {
498
        log_warn("Failed to clean work directory after build");
499
    }
500
501
    log_info("Syncing filesystem...");
502
    sync();
503
    sleep(2);
504
505
    const char *iso_path = find_latest_iso(config.out_dir);
506
    if (iso_path) {
507
        log_info("===================================");
508
        log_info("ISO created successfully!");
509
        log_info("Location: %s", iso_path);
510
        log_info("Test with: make test");
511
        log_info("===================================");
512
    } else {
513
        log_error("ISO was built but could not be found in output directory");
514
        logger_close();
515
        return 1;
516
    }
517
518
    logger_close();
519
    return 0;
520
}