From 8e433971c97960eee60ffabe78a506723a930051 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 15 Aug 2017 20:12:57 +0300 Subject: [PATCH] Improve current executable path computing. Fixes #960 (hopefully). --- Telegram/SourceFiles/_other/updater_linux.cpp | 574 +++++++++--------- Telegram/SourceFiles/application.cpp | 2 +- Telegram/SourceFiles/autoupdater.cpp | 2 +- Telegram/SourceFiles/logs.cpp | 3 +- .../platform/linux/specific_linux.cpp | 66 +- .../platform/linux/specific_linux.h | 4 +- .../SourceFiles/platform/mac/specific_mac.h | 4 +- .../SourceFiles/platform/mac/specific_mac.mm | 35 +- .../platform/mac/specific_mac_p.mm | 3 + .../SourceFiles/platform/win/specific_win.cpp | 61 +- .../SourceFiles/platform/win/specific_win.h | 4 +- .../win/windows_app_user_model_id.cpp | 2 +- Telegram/SourceFiles/settings.cpp | 21 +- 13 files changed, 406 insertions(+), 375 deletions(-) diff --git a/Telegram/SourceFiles/_other/updater_linux.cpp b/Telegram/SourceFiles/_other/updater_linux.cpp index 989e853df2..2e09dd70d8 100644 --- a/Telegram/SourceFiles/_other/updater_linux.cpp +++ b/Telegram/SourceFiles/_other/updater_linux.cpp @@ -39,16 +39,16 @@ using std::deque; using std::cout; bool do_mkdir(const char *path) { // from http://stackoverflow.com/questions/675039/how-can-i-create-directory-tree-in-c-linux - struct stat statbuf; - if (stat(path, &statbuf) != 0) { - /* Directory does not exist. EEXIST for race condition */ - if (mkdir(path, S_IRWXU) != 0 && errno != EEXIST) return false; - } else if (!S_ISDIR(statbuf.st_mode)) { - errno = ENOTDIR; - return false; - } + struct stat statbuf; + if (stat(path, &statbuf) != 0) { + /* Directory does not exist. EEXIST for race condition */ + if (mkdir(path, S_IRWXU) != 0 && errno != EEXIST) return false; + } else if (!S_ISDIR(statbuf.st_mode)) { + errno = ENOTDIR; + return false; + } - return true; + return true; } bool _debug = false; @@ -56,161 +56,164 @@ string updaterDir; string updaterName; string workDir; string exeName; +string exePath; FILE *_logFile = 0; void openLog() { - if (!_debug || _logFile) return; + if (!_debug || _logFile) return; - if (!do_mkdir((workDir + "DebugLogs").c_str())) { - return; - } + if (!do_mkdir((workDir + "DebugLogs").c_str())) { + return; + } - time_t timer; + time_t timer; - time(&timer); - struct tm *t = localtime(&timer); + time(&timer); + struct tm *t = localtime(&timer); - static const int maxFileLen = 65536; - char logName[maxFileLen]; - sprintf(logName, "%sDebugLogs/%04d%02d%02d_%02d%02d%02d_upd.txt", workDir.c_str(), - t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); - _logFile = fopen(logName, "w"); + static const int maxFileLen = 65536; + char logName[maxFileLen]; + sprintf(logName, "%sDebugLogs/%04d%02d%02d_%02d%02d%02d_upd.txt", workDir.c_str(), + t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); + _logFile = fopen(logName, "w"); } void closeLog() { - if (!_logFile) return; + if (!_logFile) return; - fclose(_logFile); - _logFile = 0; + fclose(_logFile); + _logFile = 0; } void writeLog(const char *format, ...) { - if (!_logFile) return; + if (!_logFile) { + return; + } - va_list args; - va_start(args, format); - vfprintf(_logFile, format, args); - fprintf(_logFile, "\n"); - fflush(_logFile); - va_end(args); + va_list args; + va_start(args, format); + vfprintf(_logFile, format, args); + fprintf(_logFile, "\n"); + fflush(_logFile); + va_end(args); } bool copyFile(const char *from, const char *to) { - FILE *ffrom = fopen(from, "rb"), *fto = fopen(to, "wb"); - if (!ffrom) { - if (fto) fclose(fto); - return false; - } - if (!fto) { - fclose(ffrom); - return false; - } - static const int BufSize = 65536; - char buf[BufSize]; - while (size_t size = fread(buf, 1, BufSize, ffrom)) { - fwrite(buf, 1, size, fto); - } + FILE *ffrom = fopen(from, "rb"), *fto = fopen(to, "wb"); + if (!ffrom) { + if (fto) fclose(fto); + return false; + } + if (!fto) { + fclose(ffrom); + return false; + } + static const int BufSize = 65536; + char buf[BufSize]; + while (size_t size = fread(buf, 1, BufSize, ffrom)) { + fwrite(buf, 1, size, fto); + } - struct stat fst; // from http://stackoverflow.com/questions/5486774/keeping-fileowner-and-permissions-after-copying-file-in-c - //let's say this wont fail since you already worked OK on that fp - if (fstat(fileno(ffrom), &fst) != 0) { - fclose(ffrom); - fclose(fto); - return false; - } - //update to the same uid/gid - if (fchown(fileno(fto), fst.st_uid, fst.st_gid) != 0) { - fclose(ffrom); - fclose(fto); - return false; - } - //update the permissions - if (fchmod(fileno(fto), fst.st_mode) != 0) { - fclose(ffrom); - fclose(fto); - return false; - } + struct stat fst; // from http://stackoverflow.com/questions/5486774/keeping-fileowner-and-permissions-after-copying-file-in-c + //let's say this wont fail since you already worked OK on that fp + if (fstat(fileno(ffrom), &fst) != 0) { + fclose(ffrom); + fclose(fto); + return false; + } + //update to the same uid/gid + if (fchown(fileno(fto), fst.st_uid, fst.st_gid) != 0) { + fclose(ffrom); + fclose(fto); + return false; + } + //update the permissions + if (fchmod(fileno(fto), fst.st_mode) != 0) { + fclose(ffrom); + fclose(fto); + return false; + } - fclose(ffrom); - fclose(fto); + fclose(ffrom); + fclose(fto); - return true; + return true; } bool remove_directory(const string &path) { // from http://stackoverflow.com/questions/2256945/removing-a-non-empty-directory-programmatically-in-c-or-c - DIR *d = opendir(path.c_str()); - writeLog("Removing dir '%s'", path.c_str()); + DIR *d = opendir(path.c_str()); + writeLog("Removing dir '%s'", path.c_str()); - if (!d) { - writeLog("Could not open dir '%s'", path.c_str()); - return (errno == ENOENT); - } + if (!d) { + writeLog("Could not open dir '%s'", path.c_str()); + return (errno == ENOENT); + } - while (struct dirent *p = readdir(d)) { - /* Skip the names "." and ".." as we don't want to recurse on them. */ - if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..")) continue; + while (struct dirent *p = readdir(d)) { + /* Skip the names "." and ".." as we don't want to recurse on them. */ + if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..")) continue; - string fname = path + '/' + p->d_name; - struct stat statbuf; - writeLog("Trying to get stat() for '%s'", fname.c_str()); - if (!stat(fname.c_str(), &statbuf)) { - if (S_ISDIR(statbuf.st_mode)) { - if (!remove_directory(fname.c_str())) { - closedir(d); - return false; - } - } else { - writeLog("Unlinking file '%s'", fname.c_str()); - if (unlink(fname.c_str())) { - writeLog("Failed to unlink '%s'", fname.c_str()); - closedir(d); - return false; - } - } - } else { - writeLog("Failed to call stat() on '%s'", fname.c_str()); - } - } - closedir(d); + string fname = path + '/' + p->d_name; + struct stat statbuf; + writeLog("Trying to get stat() for '%s'", fname.c_str()); + if (!stat(fname.c_str(), &statbuf)) { + if (S_ISDIR(statbuf.st_mode)) { + if (!remove_directory(fname.c_str())) { + closedir(d); + return false; + } + } else { + writeLog("Unlinking file '%s'", fname.c_str()); + if (unlink(fname.c_str())) { + writeLog("Failed to unlink '%s'", fname.c_str()); + closedir(d); + return false; + } + } + } else { + writeLog("Failed to call stat() on '%s'", fname.c_str()); + } + } + closedir(d); - writeLog("Finally removing dir '%s'", path.c_str()); - return !rmdir(path.c_str()); + writeLog("Finally removing dir '%s'", path.c_str()); + return !rmdir(path.c_str()); } bool mkpath(const char *path) { - int status = 0, pathsize = strlen(path) + 1; - char *copypath = new char[pathsize]; - memcpy(copypath, path, pathsize); + int status = 0, pathsize = strlen(path) + 1; + char *copypath = new char[pathsize]; + memcpy(copypath, path, pathsize); - char *pp = copypath, *sp; - while (status == 0 && (sp = strchr(pp, '/')) != 0) { - if (sp != pp) { - /* Neither root nor double slash in path */ - *sp = '\0'; - if (!do_mkdir(copypath)) { - delete[] copypath; - return false; - } - *sp = '/'; - } - pp = sp + 1; - } - delete[] copypath; - return do_mkdir(path); + char *pp = copypath, *sp; + while (status == 0 && (sp = strchr(pp, '/')) != 0) { + if (sp != pp) { + /* Neither root nor double slash in path */ + *sp = '\0'; + if (!do_mkdir(copypath)) { + delete[] copypath; + return false; + } + *sp = '/'; + } + pp = sp + 1; + } + delete[] copypath; + return do_mkdir(path); } bool equal(string a, string b) { - std::transform(a.begin(), a.end(), a.begin(), ::tolower); - std::transform(b.begin(), b.end(), b.begin(), ::tolower); - return a == b; + std::transform(a.begin(), a.end(), a.begin(), ::tolower); + std::transform(b.begin(), b.end(), b.begin(), ::tolower); + return a == b; } void delFolder() { - string delPathOld = workDir + "tupdates/ready", delPath = workDir + "tupdates/temp", delFolder = workDir + "tupdates"; - writeLog("Fully clearing old path '%s'..", delPathOld.c_str()); - if (!remove_directory(delPathOld)) { - writeLog("Failed to clear old path! :( New path was used?.."); - } + string delPathOld = workDir + "tupdates/ready", delPath = workDir + "tupdates/temp", delFolder = workDir + "tupdates"; + writeLog("Fully clearing old path '%s'..", delPathOld.c_str()); + if (!remove_directory(delPathOld)) { + writeLog("Failed to clear old path! :( New path was used?.."); + } writeLog("Fully clearing path '%s'..", delPath.c_str()); if (!remove_directory(delPath)) { writeLog("Error: failed to clear path! :("); @@ -219,210 +222,233 @@ void delFolder() { } bool update() { - writeLog("Update started.."); + writeLog("Update started.."); - string updDir = workDir + "tupdates/temp", readyFilePath = workDir + "tupdates/temp/ready", tdataDir = workDir + "tupdates/temp/tdata"; + string updDir = workDir + "tupdates/temp", readyFilePath = workDir + "tupdates/temp/ready", tdataDir = workDir + "tupdates/temp/tdata"; { FILE *readyFile = fopen(readyFilePath.c_str(), "rb"); if (readyFile) { fclose(readyFile); - writeLog("Ready file found! Using new path '%s'..", updDir.c_str()); - } else { - updDir = workDir + "tupdates/ready"; // old - tdataDir = workDir + "tupdates/ready/tdata"; - writeLog("Ready file not found! Using old path '%s'..", updDir.c_str()); - } + writeLog("Ready file found! Using new path '%s'..", updDir.c_str()); + } else { + updDir = workDir + "tupdates/ready"; // old + tdataDir = workDir + "tupdates/ready/tdata"; + writeLog("Ready file not found! Using old path '%s'..", updDir.c_str()); + } } - deque dirs; + deque dirs; dirs.push_back(updDir); - deque from, to, forcedirs; + deque from, to, forcedirs; do { - string dir = dirs.front(); + string dir = dirs.front(); dirs.pop_front(); - string toDir = updaterDir; + string toDir = exePath; if (dir.size() > updDir.size() + 1) { - toDir += (dir.substr(updDir.size() + 1) + '/'); + toDir += (dir.substr(updDir.size() + 1) + '/'); forcedirs.push_back(toDir); - writeLog("Parsing dir '%s' in update tree..", toDir.c_str()); + writeLog("Parsing dir '%s' in update tree..", toDir.c_str()); } - DIR *d = opendir(dir.c_str()); - if (!d) { - writeLog("Failed to open dir %s", dir.c_str()); - return false; - } + DIR *d = opendir(dir.c_str()); + if (!d) { + writeLog("Failed to open dir %s", dir.c_str()); + return false; + } - while (struct dirent *p = readdir(d)) { - /* Skip the names "." and ".." as we don't want to recurse on them. */ - if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..")) continue; + while (struct dirent *p = readdir(d)) { + /* Skip the names "." and ".." as we don't want to recurse on them. */ + if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..")) continue; - string fname = dir + '/' + p->d_name; - struct stat statbuf; - if (fname.substr(0, tdataDir.size()) == tdataDir && (fname.size() <= tdataDir.size() || fname.at(tdataDir.size()) == '/')) { - writeLog("Skipping 'tdata' path '%s'", fname.c_str()); - } else if (!stat(fname.c_str(), &statbuf)) { - if (S_ISDIR(statbuf.st_mode)) { - dirs.push_back(fname); - writeLog("Added dir '%s' in update tree..", fname.c_str()); - } else { - string tofname = updaterDir + fname.substr(updDir.size() + 1); - if (equal(tofname, updaterName)) { // bad update - has Updater - delete all dir - writeLog("Error: bad update, has Updater! '%s' equal '%s'", tofname.c_str(), updaterName.c_str()); - delFolder(); - return false; - } else if (equal(tofname, updaterDir + "Telegram") && exeName != "Telegram") { - string fullBinaryPath = updaterDir + exeName; + string fname = dir + '/' + p->d_name; + struct stat statbuf; + if (fname.substr(0, tdataDir.size()) == tdataDir && (fname.size() <= tdataDir.size() || fname.at(tdataDir.size()) == '/')) { + writeLog("Skipping 'tdata' path '%s'", fname.c_str()); + } else if (!stat(fname.c_str(), &statbuf)) { + if (S_ISDIR(statbuf.st_mode)) { + dirs.push_back(fname); + writeLog("Added dir '%s' in update tree..", fname.c_str()); + } else { + string tofname = exePath + fname.substr(updDir.size() + 1); + if (equal(tofname, updaterName)) { // bad update - has Updater - delete all dir + writeLog("Error: bad update, has Updater! '%s' equal '%s'", tofname.c_str(), updaterName.c_str()); + delFolder(); + return false; + } else if (equal(tofname, exePath + "Telegram") && exeName != "Telegram") { + string fullBinaryPath = exePath + exeName; writeLog("Target binary found: '%s', changing to '%s'", tofname.c_str(), fullBinaryPath.c_str()); tofname = fullBinaryPath; } if (fname == readyFilePath) { writeLog("Skipped ready file '%s'", fname.c_str()); - } else { + } else { from.push_back(fname); to.push_back(tofname); writeLog("Added file '%s' to be copied to '%s'", fname.c_str(), tofname.c_str()); } - } - } else { - writeLog("Could not get stat() for file %s", fname.c_str()); - } - } - closedir(d); + } + } else { + writeLog("Could not get stat() for file %s", fname.c_str()); + } + } + closedir(d); } while (!dirs.empty()); for (size_t i = 0; i < forcedirs.size(); ++i) { - string forcedir = forcedirs[i]; - writeLog("Forcing dir '%s'..", forcedir.c_str()); - if (!forcedir.empty() && !mkpath(forcedir.c_str())) { - writeLog("Error: failed to create dir '%s'..", forcedir.c_str()); - delFolder(); - return false; + string forcedir = forcedirs[i]; + writeLog("Forcing dir '%s'..", forcedir.c_str()); + if (!forcedir.empty() && !mkpath(forcedir.c_str())) { + writeLog("Error: failed to create dir '%s'..", forcedir.c_str()); + delFolder(); + return false; } } for (size_t i = 0; i < from.size(); ++i) { - string fname = from[i], tofname = to[i]; - writeLog("Copying file '%s' to '%s'..", fname.c_str(), tofname.c_str()); - int copyTries = 0, triesLimit = 30; - do { - if (!copyFile(fname.c_str(), tofname.c_str())) { - ++copyTries; - usleep(100000); - } else { - break; - } - } while (copyTries < triesLimit); - if (copyTries == triesLimit) { - writeLog("Error: failed to copy, asking to retry.."); - delFolder(); - return false; - } - } + string fname = from[i], tofname = to[i]; + writeLog("Copying file '%s' to '%s'..", fname.c_str(), tofname.c_str()); + int copyTries = 0, triesLimit = 30; + do { + if (!copyFile(fname.c_str(), tofname.c_str())) { + ++copyTries; + usleep(100000); + } else { + break; + } + } while (copyTries < triesLimit); + if (copyTries == triesLimit) { + writeLog("Error: failed to copy, asking to retry.."); + delFolder(); + return false; + } + } - writeLog("Update succeed! Clearing folder.."); - delFolder(); + writeLog("Update succeed! Clearing folder.."); + delFolder(); return true; } -int main(int argc, char *argv[]) { - bool needupdate = true, autostart = false, debug = false, tosettings = false, startintray = false, testmode = false; +string CurrentExecutablePath(int argc, char *argv[]) { + constexpr auto kMaxPath = 1024; + char result[kMaxPath] = { 0 }; + auto count = readlink("/proc/self/exe", result, kMaxPath); + if (count > 0) { + return string(result); + } - char *key = 0, *crashreport = 0; - for (int i = 1; i < argc; ++i) { - if (equal(argv[i], "-noupdate")) { - needupdate = false; - } else if (equal(argv[i], "-autostart")) { - autostart = true; + // Fallback to the first command line argument. + return argc ? string(argv[0]) : string(); +} + +int main(int argc, char *argv[]) { + bool needupdate = true, autostart = false, debug = false, tosettings = false, startintray = false, testmode = false; + + char *key = 0, *crashreport = 0; + for (int i = 1; i < argc; ++i) { + if (equal(argv[i], "-noupdate")) { + needupdate = false; + } else if (equal(argv[i], "-autostart")) { + autostart = true; } else if (equal(argv[i], "-debug")) { debug = _debug = true; } else if (equal(argv[i], "-startintray")) { startintray = true; } else if (equal(argv[i], "-testmode")) { testmode = true; - } else if (equal(argv[i], "-tosettings")) { - tosettings = true; - } else if (equal(argv[i], "-key") && ++i < argc) { - key = argv[i]; - } else if (equal(argv[i], "-workpath") && ++i < argc) { - workDir = argv[i]; + } else if (equal(argv[i], "-tosettings")) { + tosettings = true; + } else if (equal(argv[i], "-key") && ++i < argc) { + key = argv[i]; + } else if (equal(argv[i], "-workpath") && ++i < argc) { + workDir = argv[i]; } else if (equal(argv[i], "-crashreport") && ++i < argc) { crashreport = argv[i]; } else if (equal(argv[i], "-exename") && ++i < argc) { exeName = argv[i]; + } else if (equal(argv[i], "-exepath") && ++i < argc) { + exePath = argv[i]; } - } + } if (exeName.empty() || exeName.find('/') != string::npos) { exeName = "Telegram"; } openLog(); - writeLog("Updater started.."); - for (int i = 0; i < argc; ++i) { - writeLog("Argument: '%s'", argv[i]); - } - if (needupdate) writeLog("Need to update!"); - if (autostart) writeLog("From autostart!"); + writeLog("Updater started.."); + for (int i = 0; i < argc; ++i) { + writeLog("Argument: '%s'", argv[i]); + } + if (needupdate) writeLog("Need to update!"); + if (autostart) writeLog("From autostart!"); - updaterName = argv[0]; - writeLog("Updater binary name is: %s", updaterName.c_str()); - if (updaterName.size() >= 7) { - if (equal(updaterName.substr(updaterName.size() - 7), "Updater")) { + updaterName = CurrentExecutablePath(argc, argv); + writeLog("Updater binary full path is: %s", updaterName.c_str()); + if (exePath.empty()) { + writeLog("Executable path is not specified :("); + } else { + writeLog("Executable path: %s", exePath.c_str()); + } + if (updaterName.size() >= 7) { + if (equal(updaterName.substr(updaterName.size() - 7), "Updater")) { updaterDir = updaterName.substr(0, updaterName.size() - 7); - writeLog("Updater binary dir is: %s", updaterDir.c_str()); - if (needupdate) { - if (workDir.empty()) { // old app launched, update prepared in tupdates/ready (not in tupdates/temp) - writeLog("No workdir, trying to figure it out"); - struct passwd *pw = getpwuid(getuid()); - if (pw && pw->pw_dir && strlen(pw->pw_dir)) { - string tryDir = pw->pw_dir + string("/.TelegramDesktop/"); - struct stat statbuf; - writeLog("Trying to use '%s' as workDir, getting stat() for tupdates/ready", tryDir.c_str()); - if (!stat((tryDir + "tupdates/ready").c_str(), &statbuf)) { - writeLog("Stat got"); - if (S_ISDIR(statbuf.st_mode)) { - writeLog("It is directory, using home work dir"); - workDir = tryDir; - } - } - } - if (workDir.empty()) { - workDir = updaterDir; + writeLog("Updater binary dir is: %s", updaterDir.c_str()); + if (exePath.empty()) { + exePath = updaterDir; + writeLog("Using updater binary dir.", exePath.c_str()); + } + if (needupdate) { + if (workDir.empty()) { // old app launched, update prepared in tupdates/ready (not in tupdates/temp) + writeLog("No workdir, trying to figure it out"); + struct passwd *pw = getpwuid(getuid()); + if (pw && pw->pw_dir && strlen(pw->pw_dir)) { + string tryDir = pw->pw_dir + string("/.TelegramDesktop/"); + struct stat statbuf; + writeLog("Trying to use '%s' as workDir, getting stat() for tupdates/ready", tryDir.c_str()); + if (!stat((tryDir + "tupdates/ready").c_str(), &statbuf)) { + writeLog("Stat got"); + if (S_ISDIR(statbuf.st_mode)) { + writeLog("It is directory, using home work dir"); + workDir = tryDir; + } + } + } + if (workDir.empty()) { + workDir = exePath; - struct stat statbuf; - writeLog("Trying to use current as workDir, getting stat() for tupdates/ready"); - if (!stat("tupdates/ready", &statbuf)) { - writeLog("Stat got"); - if (S_ISDIR(statbuf.st_mode)) { - writeLog("It is directory, using current dir"); - workDir = string(); - } - } - } - } else { - writeLog("Passed workpath is '%s'", workDir.c_str()); - } - update(); - } - } else { - writeLog("Error: bad exe name!"); - } - } else { - writeLog("Error: short exe name!"); - } + struct stat statbuf; + writeLog("Trying to use current as workDir, getting stat() for tupdates/ready"); + if (!stat("tupdates/ready", &statbuf)) { + writeLog("Stat got"); + if (S_ISDIR(statbuf.st_mode)) { + writeLog("It is directory, using current dir"); + workDir = string(); + } + } + } + } else { + writeLog("Passed workpath is '%s'", workDir.c_str()); + } + update(); + } + } else { + writeLog("Error: bad exe name!"); + } + } else { + writeLog("Error: short exe name!"); + } - static const int MaxLen = 65536, MaxArgsCount = 128; + static const int MaxLen = 65536, MaxArgsCount = 128; - char path[MaxLen] = {0}; - string fullBinaryPath = updaterDir + exeName; - strcpy(path, fullBinaryPath.c_str()); + char path[MaxLen] = {0}; + string fullBinaryPath = exePath + exeName; + strcpy(path, fullBinaryPath.c_str()); - char *args[MaxArgsCount] = {0}, p_noupdate[] = "-noupdate", p_autostart[] = "-autostart", p_debug[] = "-debug", p_tosettings[] = "-tosettings", p_key[] = "-key", p_startintray[] = "-startintray", p_testmode[] = "-testmode"; - int argIndex = 0; - args[argIndex++] = path; + char *args[MaxArgsCount] = {0}, p_noupdate[] = "-noupdate", p_autostart[] = "-autostart", p_debug[] = "-debug", p_tosettings[] = "-tosettings", p_key[] = "-key", p_startintray[] = "-startintray", p_testmode[] = "-testmode"; + int argIndex = 0; + args[argIndex++] = path; if (crashreport) { args[argIndex++] = crashreport; } else { @@ -437,13 +463,13 @@ int main(int argc, char *argv[]) { args[argIndex++] = key; } } - pid_t pid = fork(); - switch (pid) { - case -1: writeLog("fork() failed!"); return 1; - case 0: execv(path, args); return 1; - } + pid_t pid = fork(); + switch (pid) { + case -1: writeLog("fork() failed!"); return 1; + case 0: execv(path, args); return 1; + } - writeLog("Executed Telegram, closing log and quitting.."); + writeLog("Executed Telegram, closing log and quitting.."); closeLog(); return 0; diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index 55ae70721f..d85b8d4d5f 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -454,7 +454,7 @@ void Application::startUpdateCheck(bool forceWait) { if (!Sandbox::started()) return; _updateCheckTimer->stop(); - if (_updateThread || _updateReply || !cAutoUpdate()) return; + if (_updateThread || _updateReply || !cAutoUpdate() || cExeName().isEmpty()) return; int32 constDelay = cBetaVersion() ? 600 : UpdateDelayConstPart, randDelay = cBetaVersion() ? 300 : UpdateDelayRandPart; int32 updateInSecs = cLastUpdateCheck() + constDelay + int32(rand() % randDelay) - unixtime(); diff --git a/Telegram/SourceFiles/autoupdater.cpp b/Telegram/SourceFiles/autoupdater.cpp index 1fdd261f85..a6aa4c2969 100644 --- a/Telegram/SourceFiles/autoupdater.cpp +++ b/Telegram/SourceFiles/autoupdater.cpp @@ -486,7 +486,7 @@ UpdateChecker::~UpdateChecker() { bool checkReadyUpdate() { QString readyFilePath = cWorkingDir() + qsl("tupdates/temp/ready"), readyPath = cWorkingDir() + qsl("tupdates/temp"); - if (!QFile(readyFilePath).exists()) { + if (!QFile(readyFilePath).exists() || cExeName().isEmpty()) { if (QDir(cWorkingDir() + qsl("tupdates/ready")).exists() || QDir(cWorkingDir() + qsl("tupdates/temp")).exists()) { UpdateChecker::clearAll(); } diff --git a/Telegram/SourceFiles/logs.cpp b/Telegram/SourceFiles/logs.cpp index e05553bbd0..2b51ec4504 100644 --- a/Telegram/SourceFiles/logs.cpp +++ b/Telegram/SourceFiles/logs.cpp @@ -369,7 +369,7 @@ namespace Logs { LOG(("Executable dir: %1, name: %2").arg(cExeDir()).arg(cExeName())); LOG(("Initial working dir: %1").arg(initialWorkingDir)); LOG(("Working dir: %1").arg(cWorkingDir())); - LOG(("Arguments: %1").arg(cArguments())); + LOG(("Command line: %1").arg(cArguments())); if (!LogsData) { LOG(("FATAL: Could not open '%1' for writing log!").arg(_logsFilePath(LogDataMain, qsl("_startXX")))); @@ -391,7 +391,6 @@ namespace Logs { for (LogsInMemoryList::const_iterator i = list.cbegin(), e = list.cend(); i != e; ++i) { if (i->first == LogDataMain) { _logsWrite(i->first, i->second); - LOG(("First: %1, %2").arg(i->first).arg(i->second)); } } } diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 20f20a5690..fd478d86ee 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -40,6 +40,27 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org using namespace Platform; using Platform::File::internal::EscapeShell; +namespace Platform { + +QString CurrentExecutablePath(int argc, char *argv[]) { + constexpr auto kMaxPath = 1024; + char result[kMaxPath] = { 0 }; + auto count = readlink("/proc/self/exe", result, kMaxPath); + if (count > 0) { + auto filename = QFile::decodeName(result); + auto deletedPostfix = qstr(" (deleted)"); + if (filename.endsWith(deletedPostfix) && !QFileInfo(filename).exists()) { + filename.chop(deletedPostfix.size()); + } + return filename; + } + + // Fallback to the first command line argument. + return argc ? QFile::decodeName(argv[0]) : QString(); +} + +} // namespace Platform + namespace { class _PsEventFilter : public QAbstractNativeEventFilter { @@ -104,7 +125,7 @@ QString demanglestr(const QString &mangled) { QStringList addr2linestr(uint64 *addresses, int count) { QStringList result; - if (!count) return result; + if (!count || cExeName().isEmpty()) return result; result.reserve(count); QByteArray cmd = "addr2line -e " + EscapeShell(QFile::encodeName(cExeDir() + cExeName())); @@ -306,34 +327,6 @@ QString psDownloadPath() { return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + '/' + str_const_toString(AppName) + '/'; } -QString psCurrentExeDirectory(int argc, char *argv[]) { - QString first = argc ? QFile::decodeName(argv[0]) : QString(); - if (!first.isEmpty()) { - QFileInfo info(first); - if (info.isSymLink()) { - info = info.symLinkTarget(); - } - if (info.exists()) { - return QDir(info.absolutePath()).absolutePath() + '/'; - } - } - return QString(); -} - -QString psCurrentExeName(int argc, char *argv[]) { - QString first = argc ? QFile::decodeName(argv[0]) : QString(); - if (!first.isEmpty()) { - QFileInfo info(first); - if (info.isSymLink()) { - info = info.symLinkTarget(); - } - if (info.exists()) { - return info.fileName(); - } - } - return QString(); -} - void psDoCleanup() { try { psAutoStart(false, true); @@ -431,7 +424,7 @@ bool _psRunCommand(const QByteArray &command) { void psRegisterCustomScheme() { #ifndef TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME auto home = getHomeDir(); - if (home.isEmpty() || cBetaVersion()) return; // don't update desktop file for beta version + if (home.isEmpty() || cBetaVersion() || cExeName().isEmpty()) return; // don't update desktop file for beta version #ifndef TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION DEBUG_LOG(("App Info: placing .desktop file")); @@ -533,10 +526,13 @@ void psNewVersion() { } bool _execUpdater(bool update = true, const QString &crashreport = QString()) { + if (cExeName().isEmpty()) { + return false; + } static const int MaxLen = 65536, MaxArgsCount = 128; char path[MaxLen] = {0}; - QByteArray data(QFile::encodeName(cExeDir() + (update ? "Updater" : gExeName))); + QByteArray data(QFile::encodeName(cExeDir() + (update ? "Updater" : cExeName()))); memcpy(path, data.constData(), data.size()); char *args[MaxArgsCount] = { 0 }; @@ -554,6 +550,8 @@ bool _execUpdater(bool update = true, const QString &crashreport = QString()) { char p_crashreportbuf[MaxLen] = { 0 }; char p_exe[] = "-exename"; char p_exebuf[MaxLen] = { 0 }; + char p_exepath[] = "-exepath"; + char p_exepathbuf[MaxLen] = { 0 }; int argIndex = 0; args[argIndex++] = path; if (!update) { @@ -592,6 +590,12 @@ bool _execUpdater(bool update = true, const QString &crashreport = QString()) { args[argIndex++] = p_exe; args[argIndex++] = p_exebuf; } + QByteArray exepathf = QFile::encodeName(cExeDir()); + if (exepathf.size() > 0 && exepathf.size() < MaxLen) { + memcpy(p_exepathbuf, exepathf.constData(), exepathf.size()); + args[argIndex++] = p_exepath; + args[argIndex++] = p_exepathbuf; + } Logs::closeMain(); SignalHandlers::finish(); diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.h b/Telegram/SourceFiles/platform/linux/specific_linux.h index 1796d35192..f66519431d 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.h +++ b/Telegram/SourceFiles/platform/linux/specific_linux.h @@ -37,6 +37,8 @@ inline void DeInitOnTopPanel(QWidget *panel) { inline void ReInitOnTopPanel(QWidget *panel) { } +QString CurrentExecutablePath(int argc, char *argv[]); + } // namespace Platform inline QString psServerPrefix() { @@ -65,8 +67,6 @@ void psActivateProcess(uint64 pid = 0); QString psLocalServerPrefix(); QString psAppDataPath(); QString psDownloadPath(); -QString psCurrentExeDirectory(int argc, char *argv[]); -QString psCurrentExeName(int argc, char *argv[]); void psAutoStart(bool start, bool silent = false); void psSendToMenu(bool send, bool silent = false); diff --git a/Telegram/SourceFiles/platform/mac/specific_mac.h b/Telegram/SourceFiles/platform/mac/specific_mac.h index 00410c2bbb..79be3067ad 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac.h +++ b/Telegram/SourceFiles/platform/mac/specific_mac.h @@ -25,6 +25,8 @@ inline bool TranslucentWindowsSupported(QPoint globalPosition) { return true; } +QString CurrentExecutablePath(int argc, char *argv[]); + namespace ThirdParty { inline void start() { @@ -66,8 +68,6 @@ void psActivateProcess(uint64 pid = 0); QString psLocalServerPrefix(); QString psAppDataPath(); QString psDownloadPath(); -QString psCurrentExeDirectory(int argc, char *argv[]); -QString psCurrentExeName(int argc, char *argv[]); void psAutoStart(bool start, bool silent = false); void psSendToMenu(bool send, bool silent = false); diff --git a/Telegram/SourceFiles/platform/mac/specific_mac.mm b/Telegram/SourceFiles/platform/mac/specific_mac.mm index 54206e7288..507b898d0c 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac.mm +++ b/Telegram/SourceFiles/platform/mac/specific_mac.mm @@ -35,6 +35,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include #include #include +#include namespace { @@ -82,7 +83,7 @@ void psBringToBack(QWidget *w) { } QAbstractNativeEventFilter *psNativeEventFilter() { - delete _psEventFilter; + delete _psEventFilter; _psEventFilter = new _PsEventFilter(); return _psEventFilter; } @@ -133,7 +134,7 @@ QString escapeShell(const QString &str) { QStringList atosstr(uint64 *addresses, int count, uint64 base) { QStringList result; - if (!count) return result; + if (!count || cExeName().isEmpty()) return result; result.reserve(count); QString cmdstr = "atos -o " + escapeShell(cExeDir() + cExeName()) + qsl("/Contents/MacOS/Telegram -l 0x%1").arg(base, 0, 16); @@ -316,11 +317,11 @@ TimeMs psIdleTime() { } QStringList psInitLogs() { - return _initLogs; + return _initLogs; } void psClearInitLogs() { - _initLogs = QStringList(); + _initLogs = QStringList(); } void psActivateProcess(uint64 pid) { @@ -337,28 +338,6 @@ QString psDownloadPath() { return objc_downloadPath(); } -QString psCurrentExeDirectory(int argc, char *argv[]) { - QString first = argc ? fromUtf8Safe(argv[0]) : QString(); - if (!first.isEmpty()) { - QFileInfo info(first); - if (info.exists()) { - return QDir(info.absolutePath() + qsl("/../../..")).absolutePath() + '/'; - } - } - return QString(); -} - -QString psCurrentExeName(int argc, char *argv[]) { - QString first = argc ? fromUtf8Safe(argv[0]) : QString(); - if (!first.isEmpty()) { - QFileInfo info(first); - if (info.exists()) { - return QDir(QDir(info.absolutePath() + qsl("/../..")).absolutePath()).dirName(); - } - } - return QString(); -} - void psDoCleanup() { try { psAutoStart(false, true); @@ -422,6 +401,10 @@ QString SystemLanguage() { return QString(); } +QString CurrentExecutablePath(int argc, char *argv[]) { + return NS2QString([[NSBundle mainBundle] bundlePath]); +} + } // namespace Platform void psNewVersion() { diff --git a/Telegram/SourceFiles/platform/mac/specific_mac_p.mm b/Telegram/SourceFiles/platform/mac/specific_mac_p.mm index f055b923d9..bb5824b422 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac_p.mm +++ b/Telegram/SourceFiles/platform/mac/specific_mac_p.mm @@ -463,6 +463,9 @@ bool objc_execUpdater() { } void objc_execTelegram(const QString &crashreport) { + if (cExeName().isEmpty()) { + return; + } #ifndef OS_MAC_STORE _execUpdater(NO, crashreport); #else // OS_MAC_STORE diff --git a/Telegram/SourceFiles/platform/win/specific_win.cpp b/Telegram/SourceFiles/platform/win/specific_win.cpp index dfc46e6978..9d04deaafe 100644 --- a/Telegram/SourceFiles/platform/win/specific_win.cpp +++ b/Telegram/SourceFiles/platform/win/specific_win.cpp @@ -211,34 +211,6 @@ QString psDownloadPath() { return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + '/' + str_const_toString(AppName) + '/'; } -QString psCurrentExeDirectory(int argc, char *argv[]) { - LPWSTR *args; - int argsCount; - args = CommandLineToArgvW(GetCommandLine(), &argsCount); - if (args) { - QFileInfo info(QDir::fromNativeSeparators(QString::fromWCharArray(args[0]))); - if (info.isFile()) { - return info.absoluteDir().absolutePath() + '/'; - } - LocalFree(args); - } - return QString(); -} - -QString psCurrentExeName(int argc, char *argv[]) { - LPWSTR *args; - int argsCount; - args = CommandLineToArgvW(GetCommandLine(), &argsCount); - if (args) { - QFileInfo info(QDir::fromNativeSeparators(QString::fromWCharArray(args[0]))); - if (info.isFile()) { - return info.fileName(); - } - LocalFree(args); - } - return QString(); -} - void psDoCleanup() { try { psAutoStart(false, true); @@ -374,6 +346,24 @@ QString SystemCountry() { return QString(); } +QString CurrentExecutablePath(int argc, char *argv[]) { + WCHAR result[MAX_PATH + 1] = { 0 }; + auto count = GetModuleFileName(nullptr, result, MAX_PATH + 1); + if (count < MAX_PATH + 1) { + auto info = QFileInfo(QDir::fromNativeSeparators(QString::fromWCharArray(result))); + return info.absoluteFilePath(); + } + + // Fallback to the first command line argument. + auto argsCount = 0; + if (auto args = CommandLineToArgvW(GetCommandLine(), &argsCount)) { + auto info = QFileInfo(QDir::fromNativeSeparators(QString::fromWCharArray(args[0]))); + LocalFree(args); + return info.absoluteFilePath(); + } + return QString(); +} + namespace { QString GetLangCodeById(unsigned lngId) { @@ -592,6 +582,9 @@ namespace { } void RegisterCustomScheme() { + if (cExeName().isEmpty()) { + return; + } #ifndef TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME DEBUG_LOG(("App Info: Checking custom scheme 'tg'...")); @@ -647,6 +640,10 @@ void psNewVersion() { } void psExecUpdater() { + if (cExeName().isEmpty()) { + return; + } + QString targs = qsl("-update -exename \"") + cExeName() + '"'; if (cLaunchMode() == LaunchModeAutoStart) targs += qsl(" -autostart"); if (cDebug()) targs += qsl(" -debug"); @@ -666,6 +663,9 @@ void psExecUpdater() { } void psExecTelegram(const QString &crashreport) { + if (cExeName().isEmpty()) { + return; + } QString targs = crashreport.isEmpty() ? qsl("-noupdate") : ('"' + crashreport + '"'); if (crashreport.isEmpty()) { if (cRestartingToSettings()) targs += qsl(" -tosettings"); @@ -687,6 +687,9 @@ void psExecTelegram(const QString &crashreport) { } void _manageAppLnk(bool create, bool silent, int path_csidl, const wchar_t *args, const wchar_t *description) { + if (cExeName().isEmpty()) { + return; + } WCHAR startupFolder[MAX_PATH]; HRESULT hr = SHGetFolderPath(0, path_csidl, 0, SHGFP_TYPE_CURRENT, startupFolder); if (SUCCEEDED(hr)) { @@ -1113,7 +1116,7 @@ void psWriteDump() { char ImageHlpSymbol64[sizeof(IMAGEHLP_SYMBOL64) + StackEntryMaxNameLength]; QString psPrepareCrashDump(const QByteArray &crashdump, QString dumpfile) { - if (!LoadDbgHelp(true)) { + if (!LoadDbgHelp(true) || cExeName().isEmpty()) { return qsl("ERROR: could not init dbghelp.dll!"); } diff --git a/Telegram/SourceFiles/platform/win/specific_win.h b/Telegram/SourceFiles/platform/win/specific_win.h index 3c48db3f27..66de3d74d9 100644 --- a/Telegram/SourceFiles/platform/win/specific_win.h +++ b/Telegram/SourceFiles/platform/win/specific_win.h @@ -43,6 +43,8 @@ inline void DeInitOnTopPanel(QWidget *panel) { inline void ReInitOnTopPanel(QWidget *panel) { } +QString CurrentExecutablePath(int argc, char *argv[]); + namespace ThirdParty { inline void start() { @@ -78,8 +80,6 @@ QString psLocalServerPrefix(); QString psAppDataPath(); QString psAppDataPathOld(); QString psDownloadPath(); -QString psCurrentExeDirectory(int argc, char *argv[]); -QString psCurrentExeName(int argc, char *argv[]); void psAutoStart(bool start, bool silent = false); void psSendToMenu(bool send, bool silent = false); diff --git a/Telegram/SourceFiles/platform/win/windows_app_user_model_id.cpp b/Telegram/SourceFiles/platform/win/windows_app_user_model_id.cpp index e04e502834..8eef2a47c2 100644 --- a/Telegram/SourceFiles/platform/win/windows_app_user_model_id.cpp +++ b/Telegram/SourceFiles/platform/win/windows_app_user_model_id.cpp @@ -263,7 +263,7 @@ bool validateShortcutAt(const QString &path) { bool validateShortcut() { QString path = systemShortcutPath(); - if (path.isEmpty()) return false; + if (path.isEmpty() || cExeName().isEmpty()) return false; if (cBetaVersion()) { path += qsl("TelegramBeta.lnk"); diff --git a/Telegram/SourceFiles/settings.cpp b/Telegram/SourceFiles/settings.cpp index 1ed6babfe0..2949e42f5e 100644 --- a/Telegram/SourceFiles/settings.cpp +++ b/Telegram/SourceFiles/settings.cpp @@ -163,13 +163,26 @@ void settingsParseArgs(int argc, char *argv[]) { QStringList args; for (int32 i = 0; i < argc; ++i) { - args.push_back('"' + fromUtf8Safe(argv[i]) + '"'); + args.push_back(fromUtf8Safe(argv[i])); } gArguments = args.join(' '); - gExeDir = psCurrentExeDirectory(argc, argv); - gExeName = psCurrentExeName(argc, argv); - for (int32 i = 0; i < argc; ++i) { + auto path = Platform::CurrentExecutablePath(argc, argv); + LOG(("Executable path before check: %1").arg(path)); + if (!path.isEmpty()) { + auto info = QFileInfo(path); + if (info.isSymLink()) { + info = info.symLinkTarget(); + } + if (info.exists()) { + gExeDir = info.absoluteDir().absolutePath() + '/'; + gExeName = info.fileName(); + } + } + if (cExeName().isEmpty()) { + LOG(("WARNING: Could not compute executable path, some features will be disabled.")); + } + for (auto i = 0; i != argc; ++i) { if (qstr("-testmode") == argv[i]) { gTestMode = true; } else if (qstr("-debug") == argv[i]) {