#include #include #include #include #include #include #include #include #include #include #include #include #include #include #define LIB_FLAG_GTK2 (1 << 0) #define LIB_FLAG_GTK3 (1 << 1) #define LIB_FLAG_GTK4 (1 << 2) #define LIB_FLAG_QT4 (1 << 3) #define LIB_FLAG_QT5 (1 << 4) #define LIB_FLAG_QT6 (1 << 5) #define LIB_FLAG_GLADE2 (1 << 30) #define LIB_FLAG_GTK (LIB_FLAG_GTK2 | LIB_FLAG_GTK3 | LIB_FLAG_GTK4) #define LIB_FLAG_QT (LIB_FLAG_QT4 | LIB_FLAG_QT5 | LIB_FLAG_QT6) #define MAX_PARAS 64 #define MAX_LOG_BUF (1024 * 1024) #define VTOY_GUI_PATH "_vtoy_gui_path_=" #define VTOY_ENV_STR "_vtoy_env_str_=" #define LD_CACHE_FILE "/etc/ld.so.cache" #define INT2STR_YN(a) ((a) == 0 ? "NO" : "YES") static char *g_log_buf = NULL; extern char ** environ; #define CACHEMAGIC "ld.so-1.7.0" struct file_entry { int flags; /* This is 1 for an ELF library. */ unsigned int key, value; /* String table indices. */ }; struct cache_file { char magic[sizeof CACHEMAGIC - 1]; unsigned int nlibs; struct file_entry libs[0]; }; #define CACHEMAGIC_NEW "glibc-ld.so.cache" #define CACHE_VERSION "1.1" #define CACHEMAGIC_VERSION_NEW CACHEMAGIC_NEW CACHE_VERSION struct file_entry_new { int32_t flags; /* This is 1 for an ELF library. */ uint32_t key, value; /* String table indices. */ uint32_t osversion; /* Required OS version. */ uint64_t hwcap; /* Hwcap entry. */ }; struct cache_file_new { char magic[sizeof CACHEMAGIC_NEW - 1]; char version[sizeof CACHE_VERSION - 1]; uint32_t nlibs; /* Number of entries. */ uint32_t len_strings; /* Size of string table. */ uint32_t unused[5]; /* Leave space for future extensions and align to 8 byte boundary. */ struct file_entry_new libs[0]; /* Entries describing libraries. */ /* After this the string table of size len_strings is found. */ }; /* Used to align cache_file_new. */ #define ALIGN_CACHE(addr) \ (((addr) + __alignof__ (struct cache_file_new) -1) \ & (~(__alignof__ (struct cache_file_new) - 1))) static void vlog(const char *Fmt, ...) { int buflen; char *buf = NULL; char log[512]; va_list arg; time_t stamp; struct tm ttm; FILE *fp; time(&stamp); localtime_r(&stamp, &ttm); if (g_log_buf) { buf = g_log_buf; buflen = MAX_LOG_BUF; } else { buf = log; buflen = sizeof(log); } va_start(arg, Fmt); vsnprintf(buf, buflen, Fmt, arg); va_end(arg); fp = fopen("log.txt", "a+"); if (fp) { fprintf(fp, "[%04u/%02u/%02u %02u:%02u:%02u] %s", ttm.tm_year + 1900, ttm.tm_mon, ttm.tm_mday, ttm.tm_hour, ttm.tm_min, ttm.tm_sec, buf); fclose(fp); } printf("[%04u/%02u/%02u %02u:%02u:%02u] %s", ttm.tm_year + 1900, ttm.tm_mon, ttm.tm_mday, ttm.tm_hour, ttm.tm_min, ttm.tm_sec, buf); } static int is_gtk_env(void) { const char *env = NULL; env = getenv("GNOME_SETUP_DISPLAY"); if (env && env[0] == ':') { vlog("GNOME_SETUP_DISPLAY=%s\n", env); return 1; } env = getenv("DESKTOP_SESSION"); if (env && strcasecmp(env, "xfce") == 0) { vlog("DESKTOP_SESSION=%s\n", env); return 1; } return 0; } static int is_qt_env(void) { return 0; } static int detect_gtk_version(int libflag) { int gtk2; int gtk3; int gtk4; int glade2; gtk2 = libflag & LIB_FLAG_GTK2; gtk3 = libflag & LIB_FLAG_GTK3; gtk4 = libflag & LIB_FLAG_GTK4; glade2 = libflag & LIB_FLAG_GLADE2; if (gtk2 > 0 && glade2 > 0 && (gtk3 == 0 && gtk4 == 0)) { return 2; } if (gtk3 > 0 && (gtk2 == 0 && gtk4 == 0)) { return 3; } if (gtk4 > 0 && (gtk2 == 0 && gtk3 == 0)) { return 4; } if (gtk3 > 0) { return 3; } if (gtk4 > 0) { return 4; } if (gtk2 > 0 && glade2 > 0) { return 2; } return 0; } static int detect_qt_version(int libflag) { int qt4; int qt5; int qt6; qt4 = libflag & LIB_FLAG_QT4; qt5 = libflag & LIB_FLAG_QT5; qt6 = libflag & LIB_FLAG_QT6; if (qt4 > 0 && (qt5 == 0 && qt6 == 0)) { return 4; } if (qt5 > 0 && (qt4 == 0 && qt6 == 0)) { return 5; } if (qt6 > 0 && (qt4 == 0 && qt5 == 0)) { return 6; } if (qt5 > 0) { return 5; } if (qt4 > 0) { return 4; } if (qt6 > 0) { return 6; } return 0; } int bit_from_machine(const char *machine) { if (strstr(machine, "64")) { return 64; } else { return 32; } } int get_os_bit(int *bit) { int ret; struct utsname unameData; memset(&unameData, 0, sizeof(unameData)); ret = uname(&unameData); if (ret != 0) { vlog("uname error, code: %d\n", errno); return 1; } *bit = strstr(unameData.machine, "64") ? 64 : 32; vlog("uname -m <%s> %dbit\n", unameData.machine, *bit); return 0; } int read_file_1st_line(const char *file, char *buffer, int buflen) { FILE *fp = NULL; fp = fopen(file, "r"); if (fp == NULL) { vlog("Failed to open file %s code:%d", file, errno); return 1; } fgets(buffer, buflen, fp); fclose(fp); return 0; } static int read_pid_cmdline(long pid, char *Buffer, int BufLen) { char path[256]; snprintf(path, sizeof(path), "/proc/%ld/cmdline", pid); return read_file_1st_line(path, Buffer, BufLen); } static int find_exe_path(const char *exe, char *pathbuf, int buflen) { int i; char path[PATH_MAX]; char *tmpptr = NULL; char *saveptr = NULL; char *newenv = NULL; const char *env = getenv("PATH"); if (NULL == env) { return 0; } newenv = strdup(env); if (!newenv) { return 0; } tmpptr = newenv; while (NULL != (tmpptr = strtok_r(tmpptr, ":", &saveptr))) { snprintf(path, sizeof(path), "%s/%s", tmpptr, exe); if (access(path, F_OK) != -1) { snprintf(pathbuf, buflen, "%s", path); free(newenv); return 1; } tmpptr = NULL; } free(newenv); return 0; } void dump_args(const char *prefix, char **argv) { int i = 0; vlog("=========%s ARGS BEGIN===========\n", prefix); while (argv[i]) { vlog("argv[%d]=<%s>\n", i, argv[i]); i++; } vlog("=========%s ARGS END===========\n", prefix); } int pre_check(void) { int ret; int bit; int buildbit; const char *env = NULL; env = getenv("DISPLAY"); if (NULL == env || env[0] != ':') { vlog("DISPLAY not exist(%p). Not in X environment.\n", env); return 1; } ret = get_os_bit(&bit); if (ret) { vlog("Failed to get os bit.\n"); return 1; } buildbit = strstr(VTOY_GUI_ARCH, "64") ? 64 : 32; vlog("Build bit is %d (%s)\n", buildbit, VTOY_GUI_ARCH); if (bit != buildbit) { vlog("Current system is %d bit (%s). Please run the correct VentoyGUI.\n", bit, VTOY_GUI_ARCH); return 1; } return 0; } static char * find_argv(int argc, char **argv, char *key) { int i; int len; len = (int)strlen(key); for (i = 0; i < argc; i++) { if (strncmp(argv[i], key, len) == 0) { return argv[i]; } } return NULL; } static int adjust_cur_dir(char *argv0) { int ret = 2; char c; char *pos = NULL; char *end = NULL; if (argv0[0] == '.') { return 1; } for (pos = argv0; pos && *pos; pos++) { if (*pos == '/') { end = pos; } } if (end) { c = *end; *end = 0; ret = chdir(argv0); *end = c; } return ret; } static char **recover_environ_param(char *env) { int i = 0; int j = 0; int k = 0; int cnt = 0; char **newenvs = NULL; for (i = 0; env[i]; i++) { if (env[i] == '\n') { cnt++; } } newenvs = malloc(sizeof(char *) * (cnt + 1)); if (!newenvs) { vlog("malloc new envs fail %d\n", cnt + 1); return NULL; } memset(newenvs, 0, sizeof(char *) * (cnt + 1)); for (j = i = 0; env[i]; i++) { if (env[i] == '\n') { env[i] = 0; newenvs[k++] = env + j; j = i + 1; } } vlog("recover environ %d %d\n", cnt, k); return newenvs; } static int restart_main(int argc, char **argv, char *guiexe) { int i = 0; int j = 0; char *para = NULL; char **envs = NULL; char *newargv[MAX_PARAS + 1] = { NULL }; para = find_argv(argc, argv, VTOY_ENV_STR); if (!para) { vlog("failed to find %s\n", VTOY_ENV_STR); return 1; } newargv[j++] = guiexe; for (i = 1; i < argc && j < MAX_PARAS; i++) { if (strncmp(argv[i], "_vtoy_", 6) != 0) { newargv[j++] = argv[i]; } } envs = recover_environ_param(para + strlen(VTOY_ENV_STR)); if (envs) { vlog("recover success, argc=%d evecve <%s>\n", j, guiexe); execve(guiexe, newargv, envs); } else { vlog("recover failed, argc=%d evecv <%s>\n", j, guiexe); execv(guiexe, newargv); } return 1; } static char *create_environ_param(const char *prefix, char **envs) { int i = 0; int cnt = 0; int envlen = 0; int prelen = 0; char *cur = NULL; char *para = NULL; prelen = strlen(prefix); for (i = 0; envs[i]; i++) { cnt++; envlen += strlen(envs[i]) + 1; } para = malloc(prelen + envlen); if (!para) { vlog("failed to malloc env str %d\n", prelen + envlen); return NULL; } cur = para; memcpy(cur, prefix, prelen); cur += prelen; for (i = 0; envs[i]; i++) { envlen = strlen(envs[i]); memcpy(cur, envs[i], envlen); cur[envlen] = '\n'; cur += envlen + 1; } vlog("create environment param %d\n", cnt); return para; } static int restart_by_pkexec(int argc, char **argv, const char *curpath, const char *exe) { int i = 0; int j = 0; char envcount[64]; char path[PATH_MAX]; char pkexec[PATH_MAX]; char exepara[PATH_MAX]; char *newargv[MAX_PARAS + 1] = { NULL }; vlog("try restart self by pkexec ...\n"); if (find_exe_path("pkexec", pkexec, sizeof(pkexec))) { vlog("Find pkexec at <%s>\n", pkexec); } else { vlog("pkexec not found\n"); return 1; } if (argv[0][0] != '/') { snprintf(path, sizeof(path), "%s/%s", curpath, argv[0]); } else { snprintf(path, sizeof(path), "%s", argv[0]); } snprintf(exepara, sizeof(exepara), "%s%s", VTOY_GUI_PATH, exe); newargv[j++] = pkexec; newargv[j++] = path; for (i = 1; i < argc && j < MAX_PARAS - 2; i++) { newargv[j++] = argv[i]; } newargv[j++] = create_environ_param(VTOY_ENV_STR, environ); newargv[j++] = exepara; dump_args("PKEXEC", newargv); execv(pkexec, newargv); return 1; } static int ld_cache_lib_check(const char *lib, int *flag) { if (((*flag) & LIB_FLAG_GTK3) == 0) { if (strncmp(lib, "libgtk-3.so", 11) == 0) { vlog("LIB:<%s>\n", lib); *flag |= LIB_FLAG_GTK3; return 0; } } if (((*flag) & LIB_FLAG_GTK2) == 0) { if (strncmp(lib, "libgtk-x11-2.0.so", 17) == 0) { vlog("LIB:<%s>\n", lib); *flag |= LIB_FLAG_GTK2; return 0; } } if (((*flag) & LIB_FLAG_GTK4) == 0) { if (strncmp(lib, "libgtk-4.so", 11) == 0) { vlog("LIB:<%s>\n", lib); *flag |= LIB_FLAG_GTK4; return 0; } } if (((*flag) & LIB_FLAG_QT4) == 0) { if (strncmp(lib, "libqt4", 6) == 0) { vlog("LIB:<%s>\n", lib); *flag |= LIB_FLAG_QT4; return 0; } } if (((*flag) & LIB_FLAG_QT5) == 0) { if (strncmp(lib, "libqt5", 6) == 0) { vlog("LIB:<%s>\n", lib); *flag |= LIB_FLAG_QT5; return 0; } } if (((*flag) & LIB_FLAG_QT6) == 0) { if (strncmp(lib, "libqt6", 6) == 0) { vlog("LIB:<%s>\n", lib); *flag |= LIB_FLAG_QT6; return 0; } } if (((*flag) & LIB_FLAG_GLADE2) == 0) { if (strncmp(lib, "libglade-2", 10) == 0) { vlog("LIB:<%s>\n", lib); *flag |= LIB_FLAG_GLADE2; return 0; } } return 0; } static int parse_ld_cache(int *flag) { int fd; int format; unsigned int i; struct stat st; size_t offset = 0; size_t cache_size = 0; const char *cache_data = NULL; struct cache_file *cache = NULL; struct cache_file_new *cache_new = NULL; *flag = 0; fd = open(LD_CACHE_FILE, O_RDONLY); if (fd < 0) { vlog("failed to open %s err:%d\n", LD_CACHE_FILE, errno); return 1; } if (fstat(fd, &st) < 0 || st.st_size == 0) { close(fd); return 1; } cache = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (cache == MAP_FAILED) { close(fd); return 1; } cache_size = st.st_size; if (cache_size < sizeof (struct cache_file)) { vlog("File is not a cache file.\n"); munmap (cache, cache_size); close(fd); return 1; } if (memcmp(cache->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1)) { /* This can only be the new format without the old one. */ cache_new = (struct cache_file_new *) cache; if (memcmp(cache_new->magic, CACHEMAGIC_NEW, sizeof CACHEMAGIC_NEW - 1) || memcmp (cache_new->version, CACHE_VERSION, sizeof CACHE_VERSION - 1)) { munmap (cache, cache_size); close(fd); return 1; } format = 1; /* This is where the strings start. */ cache_data = (const char *) cache_new; } else { /* Check for corruption, avoiding overflow. */ if ((cache_size - sizeof (struct cache_file)) / sizeof (struct file_entry) < cache->nlibs) { vlog("File is not a cache file.\n"); munmap (cache, cache_size); close(fd); return 1; } offset = ALIGN_CACHE(sizeof (struct cache_file) + (cache->nlibs * sizeof (struct file_entry))); /* This is where the strings start. */ cache_data = (const char *) &cache->libs[cache->nlibs]; /* Check for a new cache embedded in the old format. */ if (cache_size > (offset + sizeof (struct cache_file_new))) { cache_new = (struct cache_file_new *) ((void *)cache + offset); if (memcmp(cache_new->magic, CACHEMAGIC_NEW, sizeof CACHEMAGIC_NEW - 1) == 0 && memcmp(cache_new->version, CACHE_VERSION, sizeof CACHE_VERSION - 1) == 0) { cache_data = (const char *) cache_new; format = 1; } } } if (format == 0) { vlog("%d libs found in cache format 0\n", cache->nlibs); for (i = 0; i < cache->nlibs; i++) { ld_cache_lib_check(cache_data + cache->libs[i].key, flag); } } else if (format == 1) { vlog("%d libs found in cache format 1\n", cache_new->nlibs); for (i = 0; i < cache_new->nlibs; i++) { ld_cache_lib_check(cache_data + cache_new->libs[i].key, flag); } } vlog("ldconfig lib flags 0x%x\n", *flag); vlog("lib flags GLADE2:[%s] GTK2:[%s] GTK3:[%s] GTK4:[%s] QT4:[%s] QT5:[%s] QT6:[%s]\n", INT2STR_YN((*flag) & LIB_FLAG_GLADE2), INT2STR_YN((*flag) & LIB_FLAG_GTK2), INT2STR_YN((*flag) & LIB_FLAG_GTK3), INT2STR_YN((*flag) & LIB_FLAG_GTK4), INT2STR_YN((*flag) & LIB_FLAG_QT4), INT2STR_YN((*flag) & LIB_FLAG_QT5), INT2STR_YN((*flag) & LIB_FLAG_QT6)); munmap (cache, cache_size); close (fd); return 0; } static int detect_gui_exe_path(const char *curpath, char *pathbuf, int buflen) { int ret; int ver; int libflag = 0; const char *guitype = NULL; char line[256]; mode_t mode; struct stat filestat; if (access("./ventoy_gui_type", F_OK) != -1) { vlog("Get GUI type from ventoy_gui_type file.\n"); line[0] = 0; read_file_1st_line("./ventoy_gui_type", line, sizeof(line)); if (strncmp(line, "gtk2", 4) == 0) { guitype = "gtk"; ver = 2; parse_ld_cache(&libflag); if ((libflag & LIB_FLAG_GLADE2) == 0) { vlog("libglade2 is necessary for GTK2, but not found.\n"); return 1; } } else if (strncmp(line, "gtk3", 4) == 0) { guitype = "gtk"; ver = 3; } else if (strncmp(line, "gtk4", 4) == 0) { guitype = "gtk"; ver = 4; } else if (strncmp(line, "qt4", 3) == 0) { guitype = "qt"; ver = 4; } else if (strncmp(line, "qt5", 3) == 0) { guitype = "qt"; ver = 5; } else if (strncmp(line, "qt6", 3) == 0) { guitype = "qt"; ver = 6; } else { vlog("Current X environment is NOT supported.\n"); return 1; } } else { vlog("Now detect the GUI type ...\n"); parse_ld_cache(&libflag); if ((LIB_FLAG_GTK & libflag) > 0 && (LIB_FLAG_QT & libflag) == 0) { guitype = "gtk"; ver = detect_gtk_version(libflag); } else if ((LIB_FLAG_GTK & libflag) == 0 && (LIB_FLAG_QT & libflag) > 0) { guitype = "qt"; ver = detect_qt_version(libflag); } else if ((LIB_FLAG_GTK & libflag) > 0 && (LIB_FLAG_QT & libflag) > 0) { if (is_gtk_env()) { guitype = "gtk"; ver = detect_gtk_version(libflag); } else if (is_qt_env()) { guitype = "qt"; ver = detect_qt_version(libflag); } else { vlog("Current X environment is NOT supported.\n"); return 1; } } else { vlog("Current X environment is NOT supported.\n"); return 1; } } snprintf(pathbuf, buflen, "%s/tool/%s/Ventoy2Disk.%s%d", curpath, VTOY_GUI_ARCH, guitype, ver); vlog("This is %s%d X environment.\n", guitype, ver); vlog("exe = %s\n", pathbuf); if (access(pathbuf, F_OK) == -1) { vlog("%s is not exist.\n", pathbuf); return 1; } if (access(pathbuf, X_OK) == -1) { vlog("execute permission check fail, try chmod.\n", pathbuf); if (stat(pathbuf, &filestat) == 0) { mode = filestat.st_mode | S_IXUSR | S_IXGRP | S_IXOTH; ret = chmod(pathbuf, mode); vlog("old mode=%o new mode=%o ret=%d\n", filestat.st_mode, mode, ret); } } else { vlog("execute permission check success.\n"); } return 0; } int real_main(int argc, char **argv) { int ret; int euid; char *exe = NULL; char path[PATH_MAX]; char curpath[PATH_MAX]; ret = adjust_cur_dir(argv[0]); vlog("\n"); vlog("=========================================================\n"); vlog("=========================================================\n"); vlog("=============== VentoyGui %s ===============\n", VTOY_GUI_ARCH); vlog("=========================================================\n"); vlog("=========================================================\n"); euid = geteuid(); getcwd(curpath, sizeof(curpath)); vlog("pid:%ld ppid:%ld uid:%d euid:%d\n", (long)getpid(), (long)getppid(), getuid(), euid); vlog("adjust dir:%d current path:%s\n", ret, curpath); dump_args("RAW", argv); if (access("./boot/boot.img", F_OK) == -1) { vlog("Please run under the correct directory!\n"); return 1; } exe = find_argv(argc, argv, VTOY_GUI_PATH); if (exe) { if (euid != 0) { vlog("Invalid euid %d when restart.\n", euid); return 1; } return restart_main(argc, argv, exe + strlen(VTOY_GUI_PATH)); } else { if (pre_check()) { return 1; } if (detect_gui_exe_path(curpath, path, sizeof(path))) { return 1; } if (euid == 0) { vlog("We have root privileges, just exec %s\n", path); argv[0] = path; argv[1] = NULL; execv(argv[0], argv); } else { vlog("EUID check failed.\n"); /* try pkexec */ restart_by_pkexec(argc, argv, curpath, path); vlog("### Please run with root privileges. ###\n"); return 1; } } return 1; } int main(int argc, char **argv) { int ret; g_log_buf = malloc(MAX_LOG_BUF); if (!g_log_buf) { vlog("Failed to malloc log buffer %d\n", MAX_LOG_BUF); return 1; } ret = real_main(argc, argv); free(g_log_buf); return ret; }