From 9d91ab906d427f87c5115211b11f0ebef364515c Mon Sep 17 00:00:00 2001
From: Christoph Reiter <reiter.christoph@gmail.com>
Date: Fri, 21 May 2021 22:52:38 +0200
Subject: [PATCH 11/N] Core update

---
 lib/libalpm/alpm.h      |   9 +++
 lib/libalpm/package.c   |  18 ++++++
 lib/libalpm/sync.c      |  25 +++++++
 src/pacman/sighandler.c |  16 ++++-
 src/pacman/sync.c       | 140 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 207 insertions(+), 1 deletion(-)

diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h
index e7fc7bb..ee7b97c 100644
--- a/lib/libalpm/alpm.h
+++ b/lib/libalpm/alpm.h
@@ -2852,6 +2852,15 @@ int alpm_trans_release(alpm_handle_t *handle);
  * @return 0 on success, -1 on error (pm_errno is set accordingly)
  */
 int alpm_sync_sysupgrade(alpm_handle_t *handle, int enable_downgrade);
+#ifdef __MSYS__
+int alpm_sync_sysupgrade_core(alpm_handle_t *handle, int enable_downgrade);
+
+/** Informs whether a package is a core package.
+ * @param pkg the package to check
+ * @return non-zero if this is a core package, zero otherwise
+ */
+int alpm_pkg_is_core_package(const alpm_pkg_t *pkg);
+#endif
 
 /** Add a package to the transaction.
  * If the package was loaded by alpm_pkg_load(), it will be freed upon
diff --git a/lib/libalpm/package.c b/lib/libalpm/package.c
index a294d63..eda22b1 100644
--- a/lib/libalpm/package.c
+++ b/lib/libalpm/package.c
@@ -900,3 +900,21 @@ int _alpm_pkg_check_meta(alpm_pkg_t *pkg)
 
 	return error_found;
 }
+
+#ifdef __MSYS__
+int SYMEXPORT alpm_pkg_is_core_package(const alpm_pkg_t *pkg)
+{
+	if (pkg == NULL)
+		return 0;
+	return
+		strcmp(pkg->name, "bash")                == 0 ||
+		strcmp(pkg->name, "filesystem")          == 0 ||
+		strcmp(pkg->name, "mintty")              == 0 ||
+		strcmp(pkg->name, "msys2-runtime")       == 0 ||
+		strcmp(pkg->name, "msys2-runtime-devel") == 0 ||
+		strncmp(pkg->name, "msys2-runtime-",
+				strlen("msys2-runtime-"))        == 0 ||
+		strcmp(pkg->name, "pacman")              == 0 ||
+		strcmp(pkg->name, "pacman-mirrors")      == 0;
+}
+#endif
diff --git a/lib/libalpm/sync.c b/lib/libalpm/sync.c
index 1653b4b..294a4b9 100644
--- a/lib/libalpm/sync.c
+++ b/lib/libalpm/sync.c
@@ -197,7 +197,13 @@ static alpm_list_t *check_replacers(alpm_handle_t *handle, alpm_pkg_t *lpkg,
 	return replacers;
 }
 
+/** Search for packages to upgrade and add them to the transaction. */
+#ifdef __MSYS__
+static
+int SYMEXPORT do_alpm_sync_sysupgrade(alpm_handle_t *handle, int enable_downgrade, int core_update)
+#else
 int SYMEXPORT alpm_sync_sysupgrade(alpm_handle_t *handle, int enable_downgrade)
+#endif
 {
 	alpm_list_t *i, *j;
 	alpm_trans_t *trans;
@@ -211,6 +217,13 @@ int SYMEXPORT alpm_sync_sysupgrade(alpm_handle_t *handle, int enable_downgrade)
 	for(i = _alpm_db_get_pkgcache(handle->db_local); i; i = i->next) {
 		alpm_pkg_t *lpkg = i->data;
 
+#ifdef __MSYS__
+		/* Skip regular packages in core update, and core packages in regular update */
+		if(core_update != alpm_pkg_is_core_package(lpkg)) {
+			continue;
+		}
+#endif
+
 		if(alpm_pkg_find(trans->remove, lpkg->name)) {
 			_alpm_log(handle, ALPM_LOG_DEBUG, "%s is marked for removal -- skipping\n", lpkg->name);
 			continue;
@@ -252,6 +265,18 @@ int SYMEXPORT alpm_sync_sysupgrade(alpm_handle_t *handle, int enable_downgrade)
 	return 0;
 }
 
+#ifdef __MSYS__
+int SYMEXPORT alpm_sync_sysupgrade(alpm_handle_t *handle, int enable_downgrade)
+{
+	return do_alpm_sync_sysupgrade(handle, enable_downgrade, 0);
+}
+
+int SYMEXPORT alpm_sync_sysupgrade_core(alpm_handle_t *handle, int enable_downgrade)
+{
+	return do_alpm_sync_sysupgrade(handle, enable_downgrade, 1);
+}
+#endif
+
 alpm_list_t SYMEXPORT *alpm_find_group_pkgs(alpm_list_t *dbs,
 		const char *name)
 {
diff --git a/src/pacman/sighandler.c b/src/pacman/sighandler.c
index 3e869a3..fc2bac6 100644
--- a/src/pacman/sighandler.c
+++ b/src/pacman/sighandler.c
@@ -21,6 +21,10 @@
 #include <signal.h>
 #include <unistd.h>
 
+#ifdef __MSYS__
+#include <termios.h>
+#endif
+
 #include <alpm.h>
 
 #include "conf.h"
@@ -53,8 +57,11 @@ static void _reset_handler(int signum)
  */
 static void soft_interrupt_handler(int signum)
 {
-	console_cursor_move_end();
+#ifdef __MSYS__
+	struct termios term;
+#endif
 
+	console_cursor_move_end();
 	if(signum == SIGINT) {
 		const char msg[] = "\nInterrupt signal received\n";
 		xwrite(STDERR_FILENO, msg, ARRAYSIZE(msg) - 1);
@@ -69,6 +76,13 @@ static void soft_interrupt_handler(int signum)
 		return;
 	}
 	alpm_unlock(config->handle);
+#ifdef __MSYS__
+	/* restore input printing possibly disabled by core update */
+	if(tcgetattr(STDIN_FILENO, &term) == 0) {
+		term.c_lflag |= ECHO;
+		tcsetattr(STDIN_FILENO, TCSAFLUSH, &term);
+	}
+#endif
 	/* output a newline to be sure we clear any line we may be on */
 	xwrite(STDOUT_FILENO, "\n", 1);
 	_Exit(128 + signum);
diff --git a/src/pacman/sync.c b/src/pacman/sync.c
index 95340db..99712f4 100644
--- a/src/pacman/sync.c
+++ b/src/pacman/sync.c
@@ -28,6 +28,12 @@
 #include <sys/stat.h>
 #include <fnmatch.h>
 
+#ifdef __MSYS__
+#include <termios.h>
+#include <handle.h>
+#include <trans.h>
+#endif
+
 #include <alpm.h>
 #include <alpm_list.h>
 
@@ -699,8 +705,134 @@ cleanup:
 	return ret;
 }
 
+#ifdef __MSYS__
+
+/* Tries to kill all cygwin processes except this one */
+static int kill_all_other_msys_processes() {
+	DIR *dir;
+	struct dirent *ent;
+	char self_winpid[50];
+	int found_one = 0;
+	FILE *self = NULL;
+	size_t proc_entries = 0;
+	char **args = NULL;
+	size_t pos = 0;
+
+	self = fopen("/proc/self/winpid", "r");
+	if (self == NULL)
+		return -1;
+	fscanf(self, "%s", self_winpid);
+	fclose(self);
+
+	dir = opendir("/proc");
+	if (dir == NULL)
+		return -1;
+
+	while ((ent = readdir(dir)))
+		proc_entries++;
+	seekdir(dir, 0);
+
+	args = alloca(sizeof(char *) * (2 + (proc_entries * 2) + 1));
+	args[pos++] = "taskkill";
+	args[pos++] = "/F";
+
+	while ((ent = readdir (dir))) {
+		FILE *fp = NULL;
+		char winpid_path[PATH_MAX];
+
+		strcpy(winpid_path, "/proc/");
+		strcat(winpid_path, ent->d_name);
+		strcat(winpid_path, "/winpid");
+
+		fp = fopen(winpid_path, "r");
+		if (fp != NULL) {
+			char winpid[50];
+			fscanf(fp, "%s", winpid);
+
+			if (strcmp(winpid, self_winpid) != 0) {
+				args[pos++] = "/pid";
+				char *pidarg = alloca(strlen(winpid) + 1);
+				strcpy(pidarg, winpid);
+				args[pos++] = pidarg;
+				found_one = 1;
+			}
+
+			fclose(fp);
+		}
+	}
+	args[pos] = NULL;
+	closedir(dir);
+
+	if (!found_one)
+		return 0;
+
+	setenv("MSYS2_ARG_CONV_EXCL", "*", 1);
+	if (execvp(args[0], args) == -1) {
+		return -1;
+	}
+
+	return 0;
+}
+
+static int core_update(int *needed)
+{
+	int retval;
+	alpm_list_t *i;
+	alpm_list_t *core = NULL;
+
+	colon_printf(_("Starting core system upgrade...\n"));
+	alpm_logaction(config->handle, PACMAN_CALLER_PREFIX,
+			"starting core system upgrade\n");
+
+	if(alpm_sync_sysupgrade_core(config->handle, config->op_s_upgrade >= 2) == -1) {
+		pm_printf(ALPM_LOG_ERROR, "%s\n", alpm_strerror(alpm_errno(config->handle)));
+		trans_release();
+		return 1;
+	}
+
+	*needed = 0;
+	for(i = alpm_trans_get_add(config->handle); i; i = i->next) {
+		alpm_pkg_t *pkg = i->data;
+		if (alpm_pkg_is_core_package(pkg)) {
+			core = alpm_list_add(core, pkg);
+			*needed = 1;
+		}
+	}
+
+	if(!(*needed)) {
+		if (!config->print) {
+			printf(_(" there is nothing to do\n"));
+		}
+		return 0;
+	}
+
+	/* hooks are most likely doomed to fail in a core upgrade, so disable them */
+	alpm_option_set_hookdirs(config->handle, NULL);
+
+	config->handle->trans->add = core;
+	pm_printf(ALPM_LOG_WARNING, _("terminate other MSYS2 programs before proceeding\n"));
+	retval = sync_prepare_execute();
+	if(retval == 0) {
+		int response = 0;
+		do {
+			response = yesno(_("To complete this update all MSYS2 processes including this terminal will be closed. Confirm to proceed"));
+		} while(!response);
+
+		if (kill_all_other_msys_processes() != 0) {
+			pm_printf(ALPM_LOG_WARNING, _("terminating MSYS2 processes failed\n"));
+			exit(1);
+		}
+		exit(0);
+	}
+	return retval;
+}
+#endif
+
 static int sync_trans(alpm_list_t *targets)
 {
+#ifdef __MSYS__
+	int found_core_updates = 0;
+#endif
 	int retval = 0;
 	alpm_list_t *i;
 
@@ -723,6 +855,14 @@ static int sync_trans(alpm_list_t *targets)
 	}
 
 	if(config->op_s_upgrade) {
+#ifdef __MSYS__
+		if((retval = core_update(&found_core_updates))) {
+			return retval;
+		}
+		if(found_core_updates) {
+			return retval;
+		}
+#endif
 		if(!config->print) {
 			colon_printf(_("Starting full system upgrade...\n"));
 			alpm_logaction(config->handle, PACMAN_CALLER_PREFIX,
