]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - LinuxGUI/Ventoy2Disk/ventoy_gui.c
i18n: Small fix Russian language (#2473)
[Ventoy.git] / LinuxGUI / Ventoy2Disk / ventoy_gui.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdint.h>
4 #include <string.h>
5 #include <stdarg.h>
6 #include <unistd.h>
7 #include <errno.h>
8 #include <time.h>
9 #include <dirent.h>
10 #include <sys/utsname.h>
11 #include <sys/types.h>
12 #include <linux/limits.h>
13 #include <sys/stat.h>
14 #include <sys/fcntl.h>
15 #include <sys/mman.h>
16 #include "ventoy_json.h"
17
18 #define LIB_FLAG_GTK2 (1 << 0)
19 #define LIB_FLAG_GTK3 (1 << 1)
20 #define LIB_FLAG_GTK4 (1 << 2)
21 #define LIB_FLAG_QT4 (1 << 3)
22 #define LIB_FLAG_QT5 (1 << 4)
23 #define LIB_FLAG_QT6 (1 << 5)
24 #define LIB_FLAG_GLADE2 (1 << 30)
25
26 #define LIB_FLAG_GTK (LIB_FLAG_GTK2 | LIB_FLAG_GTK3 | LIB_FLAG_GTK4)
27 #define LIB_FLAG_QT (LIB_FLAG_QT4 | LIB_FLAG_QT5 | LIB_FLAG_QT6)
28
29 #define MAX_PARAS 64
30 #define MAX_LOG_BUF (1024 * 1024)
31 #define VTOY_GUI_PATH "_vtoy_gui_path_="
32 #define VTOY_ENV_STR "_vtoy_env_str_="
33 #define LD_CACHE_FILE "/etc/ld.so.cache"
34 #define INT2STR_YN(a) ((a) == 0 ? "NO" : "YES")
35
36 static int g_xdg_log = 0;
37 static int g_xdg_ini = 0;
38 static char g_log_file[PATH_MAX];
39 static char g_ini_file[PATH_MAX];
40 static char *g_log_buf = NULL;
41 extern char ** environ;
42
43 #define CACHEMAGIC "ld.so-1.7.0"
44
45 struct file_entry
46 {
47 int flags; /* This is 1 for an ELF library. */
48 unsigned int key, value; /* String table indices. */
49 };
50
51 struct cache_file
52 {
53 char magic[sizeof CACHEMAGIC - 1];
54 unsigned int nlibs;
55 struct file_entry libs[0];
56 };
57
58 #define CACHEMAGIC_NEW "glibc-ld.so.cache"
59 #define CACHE_VERSION "1.1"
60 #define CACHEMAGIC_VERSION_NEW CACHEMAGIC_NEW CACHE_VERSION
61
62 struct file_entry_new
63 {
64 int32_t flags; /* This is 1 for an ELF library. */
65 uint32_t key, value; /* String table indices. */
66 uint32_t osversion; /* Required OS version. */
67 uint64_t hwcap; /* Hwcap entry. */
68 };
69
70 struct cache_file_new
71 {
72 char magic[sizeof CACHEMAGIC_NEW - 1];
73 char version[sizeof CACHE_VERSION - 1];
74 uint32_t nlibs; /* Number of entries. */
75 uint32_t len_strings; /* Size of string table. */
76 uint32_t unused[5]; /* Leave space for future extensions
77 and align to 8 byte boundary. */
78 struct file_entry_new libs[0]; /* Entries describing libraries. */
79 /* After this the string table of size len_strings is found. */
80 };
81
82 /* Used to align cache_file_new. */
83 #define ALIGN_CACHE(addr) \
84 (((addr) + __alignof__ (struct cache_file_new) -1) \
85 & (~(__alignof__ (struct cache_file_new) - 1)))
86
87 #define vlog(fmt, args...) ventoy_syslog(0, fmt, ##args)
88
89 void ventoy_syslog(int level, const char *Fmt, ...)
90 {
91 int buflen;
92 char *buf = NULL;
93 char log[512];
94 va_list arg;
95 time_t stamp;
96 struct tm ttm;
97 FILE *fp;
98
99 (void)level;
100
101 time(&stamp);
102 localtime_r(&stamp, &ttm);
103
104 if (g_log_buf)
105 {
106 buf = g_log_buf;
107 buflen = MAX_LOG_BUF;
108 }
109 else
110 {
111 buf = log;
112 buflen = sizeof(log);
113 }
114
115 va_start(arg, Fmt);
116 vsnprintf(buf, buflen, Fmt, arg);
117 va_end(arg);
118
119 fp = fopen(g_log_file, "a+");
120 if (fp)
121 {
122 fprintf(fp, "[%04u/%02u/%02u %02u:%02u:%02u] %s",
123 ttm.tm_year + 1900, ttm.tm_mon, ttm.tm_mday,
124 ttm.tm_hour, ttm.tm_min, ttm.tm_sec,
125 buf);
126 fclose(fp);
127 }
128
129 #if 0
130 printf("[%04u/%02u/%02u %02u:%02u:%02u] %s",
131 ttm.tm_year + 1900, ttm.tm_mon, ttm.tm_mday,
132 ttm.tm_hour, ttm.tm_min, ttm.tm_sec,
133 buf);
134 #endif
135 }
136
137 static int is_gtk_env(void)
138 {
139 const char *env = NULL;
140
141 env = getenv("GNOME_SETUP_DISPLAY");
142 if (env && env[0] == ':')
143 {
144 vlog("GNOME_SETUP_DISPLAY=%s\n", env);
145 return 1;
146 }
147
148 env = getenv("DESKTOP_SESSION");
149 if (env && strcasecmp(env, "xfce") == 0)
150 {
151 vlog("DESKTOP_SESSION=%s\n", env);
152 return 1;
153 }
154
155 return 0;
156 }
157
158 static int is_qt_env(void)
159 {
160 return 0;
161 }
162
163 static int detect_gtk_version(int libflag)
164 {
165 int gtk2;
166 int gtk3;
167 int gtk4;
168 int glade2;
169
170 gtk2 = libflag & LIB_FLAG_GTK2;
171 gtk3 = libflag & LIB_FLAG_GTK3;
172 gtk4 = libflag & LIB_FLAG_GTK4;
173 glade2 = libflag & LIB_FLAG_GLADE2;
174
175 if (gtk2 > 0 && glade2 > 0 && (gtk3 == 0 && gtk4 == 0))
176 {
177 return 2;
178 }
179
180 if (gtk3 > 0 && (gtk2 == 0 && gtk4 == 0))
181 {
182 return 3;
183 }
184
185 if (gtk4 > 0 && (gtk2 == 0 && gtk3 == 0))
186 {
187 return 4;
188 }
189
190 if (gtk3 > 0)
191 {
192 return 3;
193 }
194
195 if (gtk4 > 0)
196 {
197 return 4;
198 }
199
200 if (gtk2 > 0 && glade2 > 0)
201 {
202 return 2;
203 }
204
205 return 0;
206 }
207
208 static int detect_qt_version(int libflag)
209 {
210 int qt4;
211 int qt5;
212 int qt6;
213
214 qt4 = libflag & LIB_FLAG_QT4;
215 qt5 = libflag & LIB_FLAG_QT5;
216 qt6 = libflag & LIB_FLAG_QT6;
217
218 if (qt4 > 0 && (qt5 == 0 && qt6 == 0))
219 {
220 return 4;
221 }
222
223 if (qt5 > 0 && (qt4 == 0 && qt6 == 0))
224 {
225 return 5;
226 }
227
228 if (qt6 > 0 && (qt4 == 0 && qt5 == 0))
229 {
230 return 6;
231 }
232
233 if (qt5 > 0)
234 {
235 return 5;
236 }
237
238 if (qt6 > 0)
239 {
240 return 6;
241 }
242
243 if (qt4 > 0)
244 {
245 return 4;
246 }
247
248 return 0;
249 }
250
251 int bit_from_machine(const char *machine)
252 {
253 if (strstr(machine, "64"))
254 {
255 return 64;
256 }
257 else
258 {
259 return 32;
260 }
261 }
262
263 int get_os_bit(int *bit)
264 {
265 int ret;
266 struct utsname unameData;
267
268 memset(&unameData, 0, sizeof(unameData));
269 ret = uname(&unameData);
270 if (ret != 0)
271 {
272 vlog("uname error, code: %d\n", errno);
273 return 1;
274 }
275
276 *bit = strstr(unameData.machine, "64") ? 64 : 32;
277 vlog("uname -m <%s> %dbit\n", unameData.machine, *bit);
278
279 return 0;
280 }
281
282 int read_file_1st_line(const char *file, char *buffer, int buflen)
283 {
284 FILE *fp = NULL;
285
286 fp = fopen(file, "r");
287 if (fp == NULL)
288 {
289 vlog("Failed to open file %s code:%d", file, errno);
290 return 1;
291 }
292
293 fgets(buffer, buflen, fp);
294 fclose(fp);
295 return 0;
296 }
297
298 static int read_pid_cmdline(long pid, char *Buffer, int BufLen)
299 {
300 char path[256];
301
302 snprintf(path, sizeof(path), "/proc/%ld/cmdline", pid);
303 return read_file_1st_line(path, Buffer, BufLen);
304 }
305
306 static int is_dir_exist(const char *fmt, ...)
307 {
308 va_list arg;
309 struct stat st;
310 char path[4096];
311
312 va_start(arg, fmt);
313 vsnprintf(path, sizeof(path), fmt, arg);
314 va_end(arg);
315
316 memset(&st, 0, sizeof(st));
317 if (stat(path, &st) < 0)
318 {
319 return 0;
320 }
321
322 if (st.st_mode & S_IFDIR)
323 {
324 return 1;
325 }
326
327 return 0;
328 }
329
330 static void touch_new_file(char *filename)
331 {
332 char *pos = NULL;
333 FILE *fp = NULL;
334
335 if (access(filename, F_OK) == -1)
336 {
337 for (pos = filename; *pos; pos++)
338 {
339 if (*pos == '/')
340 {
341 *pos = 0;
342 if (!is_dir_exist("%s", filename))
343 {
344 mkdir(filename, 0755);
345 }
346 *pos = '/';
347 }
348 }
349
350 fp = fopen(filename, "w+");
351 if (fp)
352 {
353 fclose(fp);
354 }
355 }
356 }
357
358 static int find_exe_path(const char *exe, char *pathbuf, int buflen)
359 {
360 int i;
361 char path[PATH_MAX];
362 char *tmpptr = NULL;
363 char *saveptr = NULL;
364 char *newenv = NULL;
365 const char *env = getenv("PATH");
366
367 if (NULL == env)
368 {
369 return 0;
370 }
371
372 newenv = strdup(env);
373 if (!newenv)
374 {
375 return 0;
376 }
377
378 tmpptr = newenv;
379 while (NULL != (tmpptr = strtok_r(tmpptr, ":", &saveptr)))
380 {
381 snprintf(path, sizeof(path), "%s/%s", tmpptr, exe);
382 if (access(path, F_OK) != -1)
383 {
384 snprintf(pathbuf, buflen, "%s", path);
385 free(newenv);
386 return 1;
387 }
388 tmpptr = NULL;
389 }
390
391 free(newenv);
392 return 0;
393 }
394
395 void dump_args(const char *prefix, char **argv)
396 {
397 int i = 0;
398
399 vlog("=========%s ARGS BEGIN===========\n", prefix);
400 while (argv[i])
401 {
402 vlog("argv[%d]=<%s>\n", i, argv[i]);
403 i++;
404 }
405 vlog("=========%s ARGS END===========\n", prefix);
406 }
407
408 int pre_check(void)
409 {
410 int ret;
411 int bit;
412 int buildbit;
413 const char *env = NULL;
414
415 env = getenv("DISPLAY");
416 if (NULL == env || env[0] != ':')
417 {
418 vlog("DISPLAY not exist(%p). Not in X environment.\n", env);
419 return 1;
420 }
421
422 ret = get_os_bit(&bit);
423 if (ret)
424 {
425 vlog("Failed to get os bit.\n");
426 return 1;
427 }
428
429 buildbit = strstr(VTOY_GUI_ARCH, "64") ? 64 : 32;
430 vlog("Build bit is %d (%s)\n", buildbit, VTOY_GUI_ARCH);
431
432 if (bit != buildbit)
433 {
434 vlog("Current system is %d bit (%s). Please run the correct VentoyGUI.\n", bit, VTOY_GUI_ARCH);
435 return 1;
436 }
437
438 return 0;
439 }
440
441 static char * find_argv(int argc, char **argv, char *key)
442 {
443 int i;
444 int len;
445
446 len = (int)strlen(key);
447 for (i = 0; i < argc; i++)
448 {
449 if (strncmp(argv[i], key, len) == 0)
450 {
451 return argv[i];
452 }
453 }
454
455 return NULL;
456 }
457
458 static int adjust_cur_dir(char *argv0)
459 {
460 int ret = 2;
461 char c;
462 char *pos = NULL;
463 char *end = NULL;
464
465 if (argv0[0] == '.')
466 {
467 return 1;
468 }
469
470 for (pos = argv0; pos && *pos; pos++)
471 {
472 if (*pos == '/')
473 {
474 end = pos;
475 }
476 }
477
478 if (end)
479 {
480 c = *end;
481 *end = 0;
482 ret = chdir(argv0);
483 *end = c;
484 }
485
486 return ret;
487 }
488
489
490
491 static char **recover_environ_param(char *env)
492 {
493 int i = 0;
494 int j = 0;
495 int k = 0;
496 int cnt = 0;
497 char **newenvs = NULL;
498
499 for (i = 0; env[i]; i++)
500 {
501 if (env[i] == '\n')
502 {
503 cnt++;
504 }
505 }
506
507 newenvs = malloc(sizeof(char *) * (cnt + 1));
508 if (!newenvs)
509 {
510 vlog("malloc new envs fail %d\n", cnt + 1);
511 return NULL;
512 }
513 memset(newenvs, 0, sizeof(char *) * (cnt + 1));
514
515 for (j = i = 0; env[i]; i++)
516 {
517 if (env[i] == '\n')
518 {
519 env[i] = 0;
520 newenvs[k++] = env + j;
521 j = i + 1;
522 }
523 }
524
525 vlog("recover environ %d %d\n", cnt, k);
526 return newenvs;
527 }
528
529 static int restart_main(int argc, char **argv, char *guiexe)
530 {
531 int i = 0;
532 int j = 0;
533 char *para = NULL;
534 char **envs = NULL;
535 char *newargv[MAX_PARAS + 1] = { NULL };
536
537 para = find_argv(argc, argv, VTOY_ENV_STR);
538 if (!para)
539 {
540 vlog("failed to find %s\n", VTOY_ENV_STR);
541 return 1;
542 }
543
544 newargv[j++] = guiexe;
545 for (i = 1; i < argc && j < MAX_PARAS; i++)
546 {
547 if (strncmp(argv[i], "_vtoy_", 6) != 0)
548 {
549 newargv[j++] = argv[i];
550 }
551 }
552
553 envs = recover_environ_param(para + strlen(VTOY_ENV_STR));
554 if (envs)
555 {
556 vlog("recover success, argc=%d evecve <%s>\n", j, guiexe);
557 dump_args("EXECVE", newargv);
558 execve(guiexe, newargv, envs);
559 }
560 else
561 {
562 vlog("recover failed, argc=%d evecv <%s>\n", j, guiexe);
563 execv(guiexe, newargv);
564 }
565
566 return 1;
567 }
568
569 static char *create_environ_param(const char *prefix, char **envs)
570 {
571 int i = 0;
572 int cnt = 0;
573 int envlen = 0;
574 int prelen = 0;
575 char *cur = NULL;
576 char *para = NULL;
577
578 prelen = strlen(prefix);
579 for (i = 0; envs[i]; i++)
580 {
581 cnt++;
582 envlen += strlen(envs[i]) + 1;
583 }
584
585 para = malloc(prelen + envlen);
586 if (!para)
587 {
588 vlog("failed to malloc env str %d\n", prelen + envlen);
589 return NULL;
590 }
591
592 cur = para;
593 memcpy(cur, prefix, prelen);
594 cur += prelen;
595
596 for (i = 0; envs[i]; i++)
597 {
598 envlen = strlen(envs[i]);
599 memcpy(cur, envs[i], envlen);
600
601 cur[envlen] = '\n';
602 cur += envlen + 1;
603 }
604
605 vlog("create environment param %d\n", cnt);
606 return para;
607 }
608
609 static int restart_by_pkexec(int argc, char **argv, const char *curpath, const char *exe)
610 {
611 int i = 0;
612 int j = 0;
613 char envcount[64];
614 char path[PATH_MAX];
615 char pkexec[PATH_MAX];
616 char exepara[PATH_MAX];
617 char *newargv[MAX_PARAS + 1] = { NULL };
618
619 vlog("try restart self by pkexec ...\n");
620
621 if (find_exe_path("pkexec", pkexec, sizeof(pkexec)))
622 {
623 vlog("Find pkexec at <%s>\n", pkexec);
624 }
625 else
626 {
627 vlog("pkexec not found\n");
628 return 1;
629 }
630
631 if (argv[0][0] != '/')
632 {
633 snprintf(path, sizeof(path), "%s/%s", curpath, argv[0]);
634 }
635 else
636 {
637 snprintf(path, sizeof(path), "%s", argv[0]);
638 }
639 snprintf(exepara, sizeof(exepara), "%s%s", VTOY_GUI_PATH, exe);
640
641 newargv[j++] = pkexec;
642 newargv[j++] = path;
643 for (i = 1; i < argc && j < MAX_PARAS; i++)
644 {
645 if (strcmp(argv[i], "--xdg") == 0)
646 {
647 continue;
648 }
649 newargv[j++] = argv[i];
650 }
651
652 if (j < MAX_PARAS)
653 {
654 newargv[j++] = create_environ_param(VTOY_ENV_STR, environ);
655 }
656
657 if (j < MAX_PARAS)
658 {
659 newargv[j++] = exepara;
660 }
661
662 if (g_xdg_log && j + 1 < MAX_PARAS)
663 {
664 newargv[j++] = "-l";
665 newargv[j++] = g_log_file;
666 }
667
668 if (g_xdg_ini && j + 1 < MAX_PARAS)
669 {
670 newargv[j++] = "-i";
671 newargv[j++] = g_ini_file;
672 }
673
674 dump_args("PKEXEC", newargv);
675 execv(pkexec, newargv);
676
677 return 1;
678 }
679
680 static int ld_cache_lib_check(const char *lib, int *flag)
681 {
682 if (((*flag) & LIB_FLAG_GTK3) == 0)
683 {
684 if (strncmp(lib, "libgtk-3.so", 11) == 0)
685 {
686 vlog("LIB:<%s>\n", lib);
687 *flag |= LIB_FLAG_GTK3;
688 return 0;
689 }
690 }
691
692 if (((*flag) & LIB_FLAG_GTK2) == 0)
693 {
694 if (strncmp(lib, "libgtk-x11-2.0.so", 17) == 0)
695 {
696 vlog("LIB:<%s>\n", lib);
697 *flag |= LIB_FLAG_GTK2;
698 return 0;
699 }
700 }
701
702 if (((*flag) & LIB_FLAG_GTK4) == 0)
703 {
704 if (strncmp(lib, "libgtk-4.so", 11) == 0)
705 {
706 vlog("LIB:<%s>\n", lib);
707 *flag |= LIB_FLAG_GTK4;
708 return 0;
709 }
710 }
711
712 if (((*flag) & LIB_FLAG_QT4) == 0)
713 {
714 if (strncmp(lib, "libQt4", 6) == 0)
715 {
716 vlog("LIB:<%s>\n", lib);
717 *flag |= LIB_FLAG_QT4;
718 return 0;
719 }
720 }
721
722 if (((*flag) & LIB_FLAG_QT5) == 0)
723 {
724 if (strncmp(lib, "libQt5", 6) == 0)
725 {
726 vlog("LIB:<%s>\n", lib);
727 *flag |= LIB_FLAG_QT5;
728 return 0;
729 }
730 }
731
732 if (((*flag) & LIB_FLAG_QT6) == 0)
733 {
734 if (strncmp(lib, "libQt6", 6) == 0)
735 {
736 vlog("LIB:<%s>\n", lib);
737 *flag |= LIB_FLAG_QT6;
738 return 0;
739 }
740 }
741
742 if (((*flag) & LIB_FLAG_GLADE2) == 0)
743 {
744 if (strncmp(lib, "libglade-2", 10) == 0)
745 {
746 vlog("LIB:<%s>\n", lib);
747 *flag |= LIB_FLAG_GLADE2;
748 return 0;
749 }
750 }
751
752 return 0;
753 }
754
755 static int parse_ld_cache(int *flag)
756 {
757 int fd;
758 int format;
759 unsigned int i;
760 struct stat st;
761 size_t offset = 0;
762 size_t cache_size = 0;
763 const char *cache_data = NULL;
764 struct cache_file *cache = NULL;
765 struct cache_file_new *cache_new = NULL;
766
767 *flag = 0;
768
769 fd = open(LD_CACHE_FILE, O_RDONLY);
770 if (fd < 0)
771 {
772 vlog("failed to open %s err:%d\n", LD_CACHE_FILE, errno);
773 return 1;
774 }
775
776 if (fstat(fd, &st) < 0 || st.st_size == 0)
777 {
778 close(fd);
779 return 1;
780 }
781
782 cache = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
783 if (cache == MAP_FAILED)
784 {
785 close(fd);
786 return 1;
787 }
788
789 cache_size = st.st_size;
790 if (cache_size < sizeof (struct cache_file))
791 {
792 vlog("File is not a cache file.\n");
793 munmap (cache, cache_size);
794 close(fd);
795 return 1;
796 }
797
798 if (memcmp(cache->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1))
799 {
800 /* This can only be the new format without the old one. */
801 cache_new = (struct cache_file_new *) cache;
802
803 if (memcmp(cache_new->magic, CACHEMAGIC_NEW, sizeof CACHEMAGIC_NEW - 1) ||
804 memcmp (cache_new->version, CACHE_VERSION, sizeof CACHE_VERSION - 1))
805 {
806 munmap (cache, cache_size);
807 close(fd);
808 return 1;
809 }
810
811 format = 1;
812 /* This is where the strings start. */
813 cache_data = (const char *) cache_new;
814 }
815 else
816 {
817 /* Check for corruption, avoiding overflow. */
818 if ((cache_size - sizeof (struct cache_file)) / sizeof (struct file_entry) < cache->nlibs)
819 {
820 vlog("File is not a cache file.\n");
821 munmap (cache, cache_size);
822 close(fd);
823 return 1;
824 }
825
826 offset = ALIGN_CACHE(sizeof (struct cache_file) + (cache->nlibs * sizeof (struct file_entry)));
827
828 /* This is where the strings start. */
829 cache_data = (const char *) &cache->libs[cache->nlibs];
830
831 /* Check for a new cache embedded in the old format. */
832 if (cache_size > (offset + sizeof (struct cache_file_new)))
833 {
834 cache_new = (struct cache_file_new *) ((void *)cache + offset);
835
836 if (memcmp(cache_new->magic, CACHEMAGIC_NEW, sizeof CACHEMAGIC_NEW - 1) == 0 &&
837 memcmp(cache_new->version, CACHE_VERSION, sizeof CACHE_VERSION - 1) == 0)
838 {
839 cache_data = (const char *) cache_new;
840 format = 1;
841 }
842 }
843 }
844
845 if (format == 0)
846 {
847 vlog("%d libs found in cache format 0\n", cache->nlibs);
848 for (i = 0; i < cache->nlibs; i++)
849 {
850 ld_cache_lib_check(cache_data + cache->libs[i].key, flag);
851 }
852 }
853 else if (format == 1)
854 {
855 vlog("%d libs found in cache format 1\n", cache_new->nlibs);
856
857 for (i = 0; i < cache_new->nlibs; i++)
858 {
859 ld_cache_lib_check(cache_data + cache_new->libs[i].key, flag);
860 }
861 }
862
863 vlog("ldconfig lib flags 0x%x\n", *flag);
864 vlog("lib flags GLADE2:[%s] GTK2:[%s] GTK3:[%s] GTK4:[%s] QT4:[%s] QT5:[%s] QT6:[%s]\n",
865 INT2STR_YN((*flag) & LIB_FLAG_GLADE2), INT2STR_YN((*flag) & LIB_FLAG_GTK2),
866 INT2STR_YN((*flag) & LIB_FLAG_GTK3), INT2STR_YN((*flag) & LIB_FLAG_GTK4),
867 INT2STR_YN((*flag) & LIB_FLAG_QT4), INT2STR_YN((*flag) & LIB_FLAG_QT5),
868 INT2STR_YN((*flag) & LIB_FLAG_QT6));
869
870 munmap (cache, cache_size);
871 close (fd);
872 return 0;
873 }
874
875 static int gui_type_check(VTOY_JSON *pstNode)
876 {
877 FILE *fp = NULL;
878 const char *env = NULL;
879 const char *arch = NULL;
880 const char *srctype = NULL;
881 const char *srcname = NULL;
882 const char *condition = NULL;
883 const char *expression = NULL;
884 char line[1024];
885
886 arch = vtoy_json_get_string_ex(pstNode, "arch");
887 srctype = vtoy_json_get_string_ex(pstNode, "type");
888 srcname = vtoy_json_get_string_ex(pstNode, "name");
889 condition = vtoy_json_get_string_ex(pstNode, "condition");
890 expression = vtoy_json_get_string_ex(pstNode, "expression");
891
892 if (srctype == NULL || srcname == NULL || condition == NULL)
893 {
894 return 0;
895 }
896
897 if (arch && NULL == strstr(arch, VTOY_GUI_ARCH))
898 {
899 return 0;
900 }
901
902 vlog("check <%s> <%s> <%s>\n", srctype, srcname, condition);
903
904 if (strcmp(srctype, "file") == 0)
905 {
906 if (access(srcname, F_OK) == -1)
907 {
908 return 0;
909 }
910
911 if (strcmp(condition, "exist") == 0)
912 {
913 vlog("File %s exist\n", srcname);
914 return 1;
915 }
916 else if (strcmp(condition, "contains") == 0)
917 {
918 fp = fopen(srcname, "r");
919 if (fp == NULL)
920 {
921 return 0;
922 }
923
924 while (fgets(line, sizeof(line), fp))
925 {
926 if (strstr(line, expression))
927 {
928 vlog("File %s contains %s\n", srcname, expression);
929 fclose(fp);
930 return 1;
931 }
932 }
933
934 fclose(fp);
935 return 0;
936 }
937 }
938 else if (strcmp(srctype, "env") == 0)
939 {
940 env = getenv(srcname);
941 if (env == NULL)
942 {
943 return 0;
944 }
945
946 if (strcmp(condition, "exist") == 0)
947 {
948 vlog("env %s exist\n", srcname);
949 return 1;
950 }
951 else if (strcmp(condition, "equal") == 0)
952 {
953 if (strcmp(expression, env) == 0)
954 {
955 vlog("env %s is %s\n", srcname, env);
956 return 1;
957 }
958 return 0;
959 }
960 else if (strcmp(condition, "contains") == 0)
961 {
962 if (strstr(env, expression))
963 {
964 vlog("env %s is %s contains %s\n", srcname, env, expression);
965 return 1;
966 }
967 return 0;
968 }
969 }
970
971 return 0;
972 }
973
974 static int read_file_to_buf(const char *FileName, int ExtLen, void **Bufer, int *BufLen)
975 {
976 int FileSize;
977 FILE *fp = NULL;
978 void *Data = NULL;
979
980 fp = fopen(FileName, "rb");
981 if (fp == NULL)
982 {
983 vlog("Failed to open file %s", FileName);
984 return 1;
985 }
986
987 fseek(fp, 0, SEEK_END);
988 FileSize = (int)ftell(fp);
989
990 Data = malloc(FileSize + ExtLen);
991 if (!Data)
992 {
993 fclose(fp);
994 return 1;
995 }
996
997 fseek(fp, 0, SEEK_SET);
998 fread(Data, 1, FileSize, fp);
999
1000 fclose(fp);
1001
1002 *Bufer = Data;
1003 *BufLen = FileSize;
1004
1005 return 0;
1006 }
1007
1008 static int distro_check_gui_env(char *type, int len, int *pver)
1009 {
1010 int size;
1011 int length;
1012 char *pBuf = NULL;
1013 VTOY_JSON *pstNode = NULL;
1014 VTOY_JSON *pstJson = NULL;
1015
1016 vlog("distro_check_gui_env ...\n");
1017
1018 if (access("./tool/distro_gui_type.json", F_OK) == -1)
1019 {
1020 vlog("distro_gui_type.json file not exist\n");
1021 return 0;
1022 }
1023
1024 read_file_to_buf("./tool/distro_gui_type.json", 1, (void **)&pBuf, &size);
1025 pBuf[size] = 0;
1026
1027 pstJson = vtoy_json_create();
1028 vtoy_json_parse(pstJson, pBuf);
1029
1030 for (pstNode = pstJson->pstChild; pstNode; pstNode = pstNode->pstNext)
1031 {
1032 if (gui_type_check(pstNode->pstChild))
1033 {
1034 length = (int)snprintf(type, len, "%s", vtoy_json_get_string_ex(pstNode->pstChild, "gui"));
1035 *pver = type[length - 1] - '0';
1036 type[length - 1] = 0;
1037 break;
1038 }
1039 }
1040
1041 vtoy_json_destroy(pstJson);
1042 return pstNode ? 1 : 0;
1043 }
1044
1045 static int detect_gui_exe_path(int argc, char **argv, const char *curpath, char *pathbuf, int buflen)
1046 {
1047 int i;
1048 int ret;
1049 int ver;
1050 int libflag = 0;
1051 const char *guitype = NULL;
1052 char line[256];
1053 mode_t mode;
1054 struct stat filestat;
1055
1056 for (i = 1; i < argc; i++)
1057 {
1058 if (argv[i] && strcmp(argv[i], "--gtk2") == 0)
1059 {
1060 guitype = "gtk";
1061 ver = 2;
1062 }
1063 else if (argv[i] && strcmp(argv[i], "--gtk3") == 0)
1064 {
1065 guitype = "gtk";
1066 ver = 3;
1067 }
1068 else if (argv[i] && strcmp(argv[i], "--gtk4") == 0)
1069 {
1070 guitype = "gtk";
1071 ver = 4;
1072 }
1073 else if (argv[i] && strcmp(argv[i], "--qt4") == 0)
1074 {
1075 guitype = "qt";
1076 ver = 4;
1077 }
1078 else if (argv[i] && strcmp(argv[i], "--qt5") == 0)
1079 {
1080 guitype = "qt";
1081 ver = 5;
1082 }
1083 else if (argv[i] && strcmp(argv[i], "--qt6") == 0)
1084 {
1085 guitype = "qt";
1086 ver = 6;
1087 }
1088 }
1089
1090 if (guitype)
1091 {
1092 vlog("Get GUI type from param <%s%d>.\n", guitype, ver);
1093 }
1094 else if (access("./ventoy_gui_type", F_OK) != -1)
1095 {
1096 vlog("Get GUI type from ventoy_gui_type file.\n");
1097
1098 line[0] = 0;
1099 read_file_1st_line("./ventoy_gui_type", line, sizeof(line));
1100 if (strncmp(line, "gtk2", 4) == 0)
1101 {
1102 guitype = "gtk";
1103 ver = 2;
1104
1105 parse_ld_cache(&libflag);
1106 if ((libflag & LIB_FLAG_GLADE2) == 0)
1107 {
1108 vlog("libglade2 is necessary for GTK2, but not found.\n");
1109 return 1;
1110 }
1111 }
1112 else if (strncmp(line, "gtk3", 4) == 0)
1113 {
1114 guitype = "gtk";
1115 ver = 3;
1116 }
1117 else if (strncmp(line, "gtk4", 4) == 0)
1118 {
1119 guitype = "gtk";
1120 ver = 4;
1121 }
1122 else if (strncmp(line, "qt4", 3) == 0)
1123 {
1124 guitype = "qt";
1125 ver = 4;
1126 }
1127 else if (strncmp(line, "qt5", 3) == 0)
1128 {
1129 guitype = "qt";
1130 ver = 5;
1131 }
1132 else if (strncmp(line, "qt6", 3) == 0)
1133 {
1134 guitype = "qt";
1135 ver = 6;
1136 }
1137 else
1138 {
1139 vlog("Current X environment is NOT supported.\n");
1140 return 1;
1141 }
1142 }
1143 else
1144 {
1145 vlog("Now detect the GUI type ...\n");
1146
1147 parse_ld_cache(&libflag);
1148
1149 if ((LIB_FLAG_GTK & libflag) > 0 && (LIB_FLAG_QT & libflag) == 0)
1150 {
1151 guitype = "gtk";
1152 ver = detect_gtk_version(libflag);
1153 }
1154 else if ((LIB_FLAG_GTK & libflag) == 0 && (LIB_FLAG_QT & libflag) > 0)
1155 {
1156 guitype = "qt";
1157 ver = detect_qt_version(libflag);
1158 }
1159 else if ((LIB_FLAG_GTK & libflag) > 0 && (LIB_FLAG_QT & libflag) > 0)
1160 {
1161 if (distro_check_gui_env(line, sizeof(line), &ver))
1162 {
1163 guitype = line;
1164 vlog("distro_check_gui <%s%d> ...\n", line, ver);
1165 }
1166 else if (is_gtk_env())
1167 {
1168 guitype = "gtk";
1169 ver = detect_gtk_version(libflag);
1170 }
1171 else if (is_qt_env())
1172 {
1173 guitype = "qt";
1174 ver = detect_qt_version(libflag);
1175 }
1176 else
1177 {
1178 vlog("Can not distinguish GTK and QT, default use GTK.\n");
1179 guitype = "gtk";
1180 ver = detect_gtk_version(libflag);
1181 }
1182 }
1183 else
1184 {
1185 vlog("Current X environment is NOT supported.\n");
1186 return 1;
1187 }
1188 }
1189
1190 snprintf(pathbuf, buflen, "%s/tool/%s/Ventoy2Disk.%s%d", curpath, VTOY_GUI_ARCH, guitype, ver);
1191
1192 vlog("This is %s%d X environment.\n", guitype, ver);
1193 vlog("exe = %s\n", pathbuf);
1194
1195 if (access(pathbuf, F_OK) == -1)
1196 {
1197 vlog("%s is not exist.\n", pathbuf);
1198 return 1;
1199 }
1200
1201 if (access(pathbuf, X_OK) == -1)
1202 {
1203 vlog("execute permission check fail, try chmod.\n", pathbuf);
1204 if (stat(pathbuf, &filestat) == 0)
1205 {
1206 mode = filestat.st_mode | S_IXUSR | S_IXGRP | S_IXOTH;
1207 ret = chmod(pathbuf, mode);
1208 vlog("old mode=%o new mode=%o ret=%d\n", filestat.st_mode, mode, ret);
1209 }
1210 }
1211 else
1212 {
1213 vlog("execute permission check success.\n");
1214 }
1215
1216 return 0;
1217 }
1218
1219 int real_main(int argc, char **argv)
1220 {
1221 int ret;
1222 int euid;
1223 char *exe = NULL;
1224 char path[PATH_MAX];
1225 char curpath[PATH_MAX];
1226
1227 ret = adjust_cur_dir(argv[0]);
1228
1229 vlog("\n");
1230 vlog("=========================================================\n");
1231 vlog("=========================================================\n");
1232 vlog("=============== VentoyGui %s ===============\n", VTOY_GUI_ARCH);
1233 vlog("=========================================================\n");
1234 vlog("=========================================================\n");
1235 vlog("log file is <%s>\n", g_log_file);
1236
1237 euid = geteuid();
1238 getcwd(curpath, sizeof(curpath));
1239
1240 vlog("pid:%ld ppid:%ld uid:%d euid:%d\n", (long)getpid(), (long)getppid(), getuid(), euid);
1241 vlog("adjust dir:%d current path:%s\n", ret, curpath);
1242 dump_args("RAW", argv);
1243
1244 if (access("./boot/boot.img", F_OK) == -1)
1245 {
1246 vlog("Please run under the correct directory!\n");
1247 return 1;
1248 }
1249
1250 exe = find_argv(argc, argv, VTOY_GUI_PATH);
1251 if (exe)
1252 {
1253 if (euid != 0)
1254 {
1255 vlog("Invalid euid %d when restart.\n", euid);
1256 return 1;
1257 }
1258
1259 return restart_main(argc, argv, exe + strlen(VTOY_GUI_PATH));
1260 }
1261 else
1262 {
1263 if (pre_check())
1264 {
1265 return 1;
1266 }
1267
1268 if (detect_gui_exe_path(argc, argv, curpath, path, sizeof(path)))
1269 {
1270 return 1;
1271 }
1272
1273 if (euid == 0)
1274 {
1275 vlog("We have root privileges, just exec %s\n", path);
1276 argv[0] = path;
1277 execv(argv[0], argv);
1278 }
1279 else
1280 {
1281 vlog("EUID check failed.\n");
1282
1283 /* try pkexec */
1284 restart_by_pkexec(argc, argv, curpath, path);
1285
1286 vlog("### Please run with root privileges. ###\n");
1287 return 1;
1288 }
1289 }
1290
1291 return 1;
1292 }
1293
1294 int main(int argc, char **argv)
1295 {
1296 int i;
1297 int ret;
1298 const char *env = NULL;
1299
1300 snprintf(g_log_file, sizeof(g_log_file), "log.txt");
1301 for (i = 0; i < argc; i++)
1302 {
1303 if (argv[i] && argv[i + 1] && strcmp(argv[i], "-l") == 0)
1304 {
1305 touch_new_file(argv[i + 1]);
1306 snprintf(g_log_file, sizeof(g_log_file), "%s", argv[i + 1]);
1307 }
1308 else if (argv[i] && argv[i + 1] && strcmp(argv[i], "-i") == 0)
1309 {
1310 touch_new_file(argv[i + 1]);
1311 }
1312 else if (argv[i] && strcmp(argv[i], "--xdg") == 0)
1313 {
1314 env = getenv("XDG_CACHE_HOME");
1315 if (env)
1316 {
1317 g_xdg_log = 1;
1318 snprintf(g_log_file, sizeof(g_log_file), "%s/ventoy/ventoy.log", env);
1319 touch_new_file(g_log_file);
1320 }
1321 else
1322 {
1323 env = getenv("HOME");
1324 if (env && is_dir_exist("%s/.cache", env))
1325 {
1326 g_xdg_log = 1;
1327 snprintf(g_log_file, sizeof(g_log_file), "%s/.cache/ventoy/ventoy.log", env);
1328 touch_new_file(g_log_file);
1329 }
1330 }
1331
1332 env = getenv("XDG_CONFIG_HOME");
1333 if (env)
1334 {
1335 g_xdg_ini = 1;
1336 snprintf(g_ini_file, sizeof(g_ini_file), "%s/ventoy/Ventoy2Disk.ini", env);
1337 touch_new_file(g_ini_file);
1338 }
1339 else
1340 {
1341 env = getenv("HOME");
1342 if (env && is_dir_exist("%s/.config", env))
1343 {
1344 g_xdg_ini = 1;
1345 snprintf(g_ini_file, sizeof(g_ini_file), "%s/.config/ventoy/Ventoy2Disk.ini", env);
1346 touch_new_file(g_ini_file);
1347 }
1348 }
1349 }
1350 }
1351
1352 g_log_buf = malloc(MAX_LOG_BUF);
1353 if (!g_log_buf)
1354 {
1355 vlog("Failed to malloc log buffer %d\n", MAX_LOG_BUF);
1356 return 1;
1357 }
1358
1359 ret = real_main(argc, argv);
1360 free(g_log_buf);
1361 return ret;
1362 }
1363