[PATCH] builtin/gc: Ignore random minute field when registering macOS services

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



In macOS, `git-maintenance` registers several launchctl services
to periodically run Git maintenance tasks by creating plist files
in `~/Library/LaunchAgents/`.
To avoid re-registering services unnecessarily, we check if a service
is already registered by verifying the existence and contents
of the corresponding plist file.

However, these plist files include a random value in the minute field
to distribute maintenance tasks over time. Because this value changes
with each registration attempt, a direct comparison of the entire file
(via `strbuf_cmp()`) often fails, causing services to be erroneously
re-registered. As a result, users may see multiple services registered
and receive repeated “Background Items Added” notifications.

To resolve this, introduce `launchctl_plist_cmp_ignore_minute()`,
which compares the content of the plist file while ignoring
the random minute field. This ensures that services are not
needlessly re-registered when the only difference in the plist file
is the randomized minute value.

Signed-off-by: Byoungchan Lee <byoungchan.lee@xxxxxxx>
---
 builtin/gc.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 47 insertions(+), 4 deletions(-)

diff --git a/builtin/gc.c b/builtin/gc.c
index a9b1c36de2..6405f4d332 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -1951,6 +1951,51 @@ static char *launchctl_get_uid(void)
  return xstrfmt("gui/%d", getuid());
 }

+/*
+ * Compare two buffers that represent launchctl property lists, but ignore
+ * lines that contain <key>Minute</key><integer>...</integer> because the
+ * minute values are not significant for comparison.
+ */
+static int launchctl_plist_cmp_ignore_minute(const struct strbuf *a,
+          const struct strbuf *b)
+{
+ char *buf_a = xstrndup(a->buf, a->len);
+ char *buf_b = xstrndup(b->buf, b->len);
+ char *line_a = buf_a;
+ char *line_b = buf_b;
+ int result = 0;
+
+ while (line_a && line_b) {
+  char *next_line_a = strchr(line_a, '\n');
+  char *next_line_b = strchr(line_b, '\n');
+
+  if (next_line_a)
+   *next_line_a = '\0';
+  if (next_line_b)
+   *next_line_b = '\0';
+
+  if (strstr(line_a, "<key>Minute</key><integer>") &&
+      strstr(line_a, "</integer>") &&
+      strstr(line_b, "<key>Minute</key><integer>") &&
+      strstr(line_b, "</integer>")) {
+   line_a = next_line_a ? next_line_a + 1 : NULL;
+   line_b = next_line_b ? next_line_b + 1 : NULL;
+   continue;
+  }
+
+  result = strcmp(line_a, line_b);
+  if (result)
+   break;
+
+  line_a = next_line_a ? next_line_a + 1 : NULL;
+  line_b = next_line_b ? next_line_b + 1 : NULL;
+ }
+
+ free(buf_a);
+ free(buf_b);
+ return result;
+}
+
 static int launchctl_boot_plist(int enable, const char *filename)
 {
  char *cmd;
@@ -2022,7 +2067,6 @@ static int launchctl_schedule_plist(const char
*exec_path, enum schedule_priorit
  struct lock_file lk = LOCK_INIT;
  static unsigned long lock_file_timeout_ms = ULONG_MAX;
  struct strbuf plist = STRBUF_INIT, plist2 = STRBUF_INIT;
- struct stat st;
  char *cmd;
  int minute = get_random_minute();

@@ -2100,9 +2144,8 @@ static int launchctl_schedule_plist(const char
*exec_path, enum schedule_priorit
   * Does this file already exist? With the intended contents? Is it
   * registered already? Then it does not need to be re-registered.
   */
- if (!stat(filename, &st) && st.st_size == plist.len &&
-     strbuf_read_file(&plist2, filename, plist.len) == plist.len &&
-     !strbuf_cmp(&plist, &plist2) &&
+ if (strbuf_read_file(&plist2, filename, plist.len) >= 0 &&
+     !launchctl_plist_cmp_ignore_minute(&plist, &plist2) &&
      launchctl_list_contains_plist(name, cmd))
   rollback_lock_file(&lk);
  else {
-- 
2.47.1





[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux