diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3775bdf..62b3acb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,7 +53,7 @@ jobs: mkdir -p out VER=${{ github.ref_type == 'tag' && github.ref_name || (github.event.inputs.version != '' && github.event.inputs.version || 'dev') }} VER=${VER#v} - ${{ matrix.cc }} -O2 -Wall -static -std=c11 -D_FORTIFY_SOURCE=0 -U_FORTIFY_SOURCE -DPMM_VERSION=\"$VER\" -o out/${{ matrix.bin }} \ + ${{ matrix.cc }} -O2 -Wall -static -std=c11 -D_GNU_SOURCE -D_FORTIFY_SOURCE=0 -U_FORTIFY_SOURCE -DPMM_VERSION=\"$VER\" -o out/${{ matrix.bin }} \ src/main.c src/json.c src/ini.c src/pmm.c src/http.c src/repo.c src/install.c \ src/sha256.c src/sha1.c src/mirrors.c src/pdm.c src/out.c src/i18n.c diff --git a/CHANGELOG/0.3.7.md b/CHANGELOG/0.3.7.md new file mode 100644 index 0000000..38b4359 --- /dev/null +++ b/CHANGELOG/0.3.7.md @@ -0,0 +1,13 @@ +# PMM v0.3.7 + +## 修复 +- **安装/升级 `pmm` 自身后「不生效」**:之前新二进制只装进 `~/.pmm/root`,从不覆盖当前正在运行的 pmm 路径, + 导致 apt 装的 `/usr/local/bin/pmm` 一直挡在前面、或升级后需 `hash -r` 才生效。现在安装 `pmm` 包时会把新二进制 + **同步覆盖到当前运行的 pmm 路径**(如 `/usr/local/bin/pmm`)并自动执行 `hash -r`,`pmm -v` 立即显示新版本。 +- Windows 上 `pmm_add_to_path` 由 **append** 改为 **prepend**:PMM 目录排在 PATH 前面,避免被 `/usr/local/bin` + 等系统目录覆盖(与 Linux 的 `~/.bashrc` 行为一致)。 +- 构建命令增加 `-D_GNU_SOURCE`,修复新版 gcc 下 `strdup`/`popen`/`realpath` 的隐式声明编译错误。 + +## 说明 +- 若运行用户无权限覆盖 `/usr/local/bin/pmm`(非 root),会打印黄色提示:新版本位于 + `~/.pmm/root/usr/local/bin/pmm`,请将其加入 PATH 并在当前 shell 运行 `hash -r`。 diff --git a/src/i18n.c b/src/i18n.c index 120d069..fe8a8b4 100644 --- a/src/i18n.c +++ b/src/i18n.c @@ -55,6 +55,7 @@ static const char *kBuiltinZh = "\"msg.checksum-ok\":\"%s 校验 OK: %s\",\"msg.checksum-mismatch\":\"校验不匹配 (%s)!\"," "\"msg.resolving-dep\":\"正在解析依赖 %s\",\"msg.registry-ok\":\"注册表 sha256 OK: %s\"," "\"msg.path-exists\":\"PATH 已包含 PMM 目录\",\"msg.err.registry-not-found\":\"找不到软件包 '%s'\"," +"\"msg.pmm-self-replaced\":\"pmm 已更新到 %s\",\"msg.pmm-self-hint\":\"无法覆盖 %s(需 root/管理员);新版本在 %s。请将其加入 PATH 并在当前 shell 运行 hash -r\"," "\"msg.err.usage\":\"用法\",\"msg.err.unknown-cmd\":\"未知命令 '%s' (试试 'pmm help')\"," "\"msg.err.download-failed\":\"下载语言包 %s 失败\",\"msg.err.invalid-locale\":\"无效语言 %s\"," "\"msg.err.not-found\":\"找不到软件包 '%s'\",\"msg.err.failed-install\":\"安装 %s 失败\"," diff --git a/src/pdm.c b/src/pdm.c index 5385152..8b7b38b 100644 --- a/src/pdm.c +++ b/src/pdm.c @@ -393,6 +393,51 @@ static void restore_aside(const char *target) { rename(old, target); } +/* Locate the freshly-installed pmm binary under `root`. Fills `out` with the + * full path and returns 0 on success, 1 if it couldn't be found. */ +static int find_pmm_binary(const char *root, char *out, size_t outsz) { +#ifdef _WIN32 + const char *names[] = { "pmm.exe", "pmm" }; +#else + const char *names[] = { "pmm" }; +#endif + const char *dirms[] = { "/bin", "/usr/local/bin", "/", NULL }; + for (int d = 0; dirms[d]; d++) { + for (size_t k = 0; k < sizeof(names) / sizeof(names[0]); k++) { + snprintf(out, outsz, "%s%s/%s", root, dirms[d], names[k]); + struct stat st; + if (stat(out, &st) == 0 && S_ISREG(st.st_mode)) return 0; + } + } + return 1; +} + +/* After installing/upgrading the `pmm` tool itself, make the newly installed + * binary take effect immediately: copy it over the currently-running pmm + * (so an apt/system /usr/local/bin/pmm is replaced) and clear the shell's + * command hash so `pmm -v` reflects the new version without `hash -r`. */ +static void pmm_sync_self(const char *newbin) { + const char *self = pmm_self_path(); + if (!self || !*self) return; + if (path_eq_ci(newbin, self)) return; /* already the running image */ + int moved = move_self_aside(self); + if (copy_file(newbin, self) != 0) { + if (moved) restore_aside(self); /* put the old image back */ + pmm_warn("%s", pmm_tr_fmt("msg.pmm-self-hint", self, newbin, newbin)); + return; + } + if (moved) { + char old[1500]; + snprintf(old, sizeof(old), "%s.old", self); + remove(old); /* drop the old image */ + } + pmm_success("%s", pmm_tr_fmt("msg.pmm-self-replaced", self)); +#ifndef _WIN32 + { char cx[1600]; snprintf(cx, sizeof(cx), "chmod +x \"%s\" 2>/dev/null || true", self); system(cx); } + system("hash -r 2>/dev/null || true"); +#endif +} + int pdm_info(const char *pdmfile) { pmm_info("%s\n", base_name(pdmfile)); /* Copy into the pm dir + chdir, then use relative names (works with both @@ -576,6 +621,15 @@ int pdm_install_file(const char *pdmfile) { } #endif + /* If we just installed/upgraded the pmm tool itself, make the new binary + * take effect immediately: replace the running image and clear the hash. + * (Otherwise an apt/system /usr/local/bin/pmm keeps shadowing it.) */ + if (pkg && strcmp(pkg, "pmm") == 0) { + char nb[1400]; + if (find_pmm_binary(root, nb, sizeof(nb)) == 0) + pmm_sync_self(nb); + } + free(pkg); if (ver) free(ver); return 0; } diff --git a/src/pmm.c b/src/pmm.c index 33963d6..b2cc258 100644 --- a/src/pmm.c +++ b/src/pmm.c @@ -393,26 +393,30 @@ void pmm_add_to_path(void) { } #endif - /* append missing dirs (bounds-safe: never let size_t underflow) */ - char next[17000] = ""; - snprintf(next, sizeof(next), "%s", cur); + /* prepend any missing PMM dirs so they take priority over /usr/local/bin + * (an apt/system install could otherwise shadow a pmm-managed binary). */ + char prefix[17000] = ""; + size_t plen = 0; int added = 0; for (int i = 0; i < n; i++) { if (!list[i][0]) continue; - if (path_has(next, list[i], ';') || path_has(next, list[i], ':')) continue; - size_t l = strlen(next); - /* ensure a separator between entries */ - if (l && next[l-1] != ';' && next[l-1] != ':' && l + 1 < sizeof(next)) { - next[l] = ';'; next[l+1] = '\0'; l++; - } - size_t room = sizeof(next) - l - 1; + if (path_has(cur, list[i], ';') || path_has(cur, list[i], ':') + || path_has(prefix, list[i], ';') || path_has(prefix, list[i], ':')) continue; + if (plen) { if (plen + 1 < sizeof(prefix)) prefix[plen++] = ';'; } + size_t room = sizeof(prefix) - plen - 1; if (room <= 0) break; /* no space left: stop */ - snprintf(next + l, room + 1, "%s", list[i]); + snprintf(prefix + plen, room + 1, "%s", list[i]); + plen += strlen(list[i]); added++; pmm_info("%s", pmm_tr_fmt("msg.path-adding", list[i])); } if (!added) { pmm_info(pmm_tr("msg.path-exists")); return; } + /* final value: PMM dirs (prefix) then the existing startup PATH (cur). + * Only Windows persists it (setx); Linux prepends via .bashrc below. */ + char next[17000] = ""; + snprintf(next, sizeof(next), "%s;%s", prefix, cur); + #ifdef _WIN32 { char cmd[17001];