Merge branch 'js/maintenance-launchctl-fix' into maint
"git maintenance" scheduler fix for macOS. * js/maintenance-launchctl-fix: maintenance: skip bootout/bootstrap when plist is registered maintenance: create `launchctl` configuration using a lock filemaint
commit
b40b6187e4
91
builtin/gc.c
91
builtin/gc.c
|
|
@ -1600,18 +1600,40 @@ static int launchctl_remove_plists(const char *cmd)
|
||||||
launchctl_remove_plist(SCHEDULE_WEEKLY, cmd);
|
launchctl_remove_plist(SCHEDULE_WEEKLY, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int launchctl_list_contains_plist(const char *name, const char *cmd)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
struct child_process child = CHILD_PROCESS_INIT;
|
||||||
|
char *uid = launchctl_get_uid();
|
||||||
|
|
||||||
|
strvec_split(&child.args, cmd);
|
||||||
|
strvec_pushl(&child.args, "list", name, NULL);
|
||||||
|
|
||||||
|
child.no_stderr = 1;
|
||||||
|
child.no_stdout = 1;
|
||||||
|
|
||||||
|
if (start_command(&child))
|
||||||
|
die(_("failed to start launchctl"));
|
||||||
|
|
||||||
|
result = finish_command(&child);
|
||||||
|
|
||||||
|
free(uid);
|
||||||
|
|
||||||
|
/* Returns failure if 'name' doesn't exist. */
|
||||||
|
return !result;
|
||||||
|
}
|
||||||
|
|
||||||
static int launchctl_schedule_plist(const char *exec_path, enum schedule_priority schedule, const char *cmd)
|
static int launchctl_schedule_plist(const char *exec_path, enum schedule_priority schedule, const char *cmd)
|
||||||
{
|
{
|
||||||
FILE *plist;
|
int i, fd;
|
||||||
int i;
|
|
||||||
const char *preamble, *repeat;
|
const char *preamble, *repeat;
|
||||||
const char *frequency = get_frequency(schedule);
|
const char *frequency = get_frequency(schedule);
|
||||||
char *name = launchctl_service_name(frequency);
|
char *name = launchctl_service_name(frequency);
|
||||||
char *filename = launchctl_service_filename(name);
|
char *filename = launchctl_service_filename(name);
|
||||||
|
struct lock_file lk = LOCK_INIT;
|
||||||
if (safe_create_leading_directories(filename))
|
static unsigned long lock_file_timeout_ms = ULONG_MAX;
|
||||||
die(_("failed to create directories for '%s'"), filename);
|
struct strbuf plist = STRBUF_INIT, plist2 = STRBUF_INIT;
|
||||||
plist = xfopen(filename, "w");
|
struct stat st;
|
||||||
|
|
||||||
preamble = "<?xml version=\"1.0\"?>\n"
|
preamble = "<?xml version=\"1.0\"?>\n"
|
||||||
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
|
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
|
||||||
|
|
@ -1630,7 +1652,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit
|
||||||
"</array>\n"
|
"</array>\n"
|
||||||
"<key>StartCalendarInterval</key>\n"
|
"<key>StartCalendarInterval</key>\n"
|
||||||
"<array>\n";
|
"<array>\n";
|
||||||
fprintf(plist, preamble, name, exec_path, exec_path, frequency);
|
strbuf_addf(&plist, preamble, name, exec_path, exec_path, frequency);
|
||||||
|
|
||||||
switch (schedule) {
|
switch (schedule) {
|
||||||
case SCHEDULE_HOURLY:
|
case SCHEDULE_HOURLY:
|
||||||
|
|
@ -1639,7 +1661,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit
|
||||||
"<key>Minute</key><integer>0</integer>\n"
|
"<key>Minute</key><integer>0</integer>\n"
|
||||||
"</dict>\n";
|
"</dict>\n";
|
||||||
for (i = 1; i <= 23; i++)
|
for (i = 1; i <= 23; i++)
|
||||||
fprintf(plist, repeat, i);
|
strbuf_addf(&plist, repeat, i);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SCHEDULE_DAILY:
|
case SCHEDULE_DAILY:
|
||||||
|
|
@ -1649,32 +1671,59 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit
|
||||||
"<key>Minute</key><integer>0</integer>\n"
|
"<key>Minute</key><integer>0</integer>\n"
|
||||||
"</dict>\n";
|
"</dict>\n";
|
||||||
for (i = 1; i <= 6; i++)
|
for (i = 1; i <= 6; i++)
|
||||||
fprintf(plist, repeat, i);
|
strbuf_addf(&plist, repeat, i);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SCHEDULE_WEEKLY:
|
case SCHEDULE_WEEKLY:
|
||||||
fprintf(plist,
|
strbuf_addstr(&plist,
|
||||||
"<dict>\n"
|
"<dict>\n"
|
||||||
"<key>Day</key><integer>0</integer>\n"
|
"<key>Day</key><integer>0</integer>\n"
|
||||||
"<key>Hour</key><integer>0</integer>\n"
|
"<key>Hour</key><integer>0</integer>\n"
|
||||||
"<key>Minute</key><integer>0</integer>\n"
|
"<key>Minute</key><integer>0</integer>\n"
|
||||||
"</dict>\n");
|
"</dict>\n");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
/* unreachable */
|
/* unreachable */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
fprintf(plist, "</array>\n</dict>\n</plist>\n");
|
strbuf_addstr(&plist, "</array>\n</dict>\n</plist>\n");
|
||||||
fclose(plist);
|
|
||||||
|
|
||||||
/* bootout might fail if not already running, so ignore */
|
if (safe_create_leading_directories(filename))
|
||||||
launchctl_boot_plist(0, filename, cmd);
|
die(_("failed to create directories for '%s'"), filename);
|
||||||
if (launchctl_boot_plist(1, filename, cmd))
|
|
||||||
die(_("failed to bootstrap service %s"), filename);
|
if ((long)lock_file_timeout_ms < 0 &&
|
||||||
|
git_config_get_ulong("gc.launchctlplistlocktimeoutms",
|
||||||
|
&lock_file_timeout_ms))
|
||||||
|
lock_file_timeout_ms = 150;
|
||||||
|
|
||||||
|
fd = hold_lock_file_for_update_timeout(&lk, filename, LOCK_DIE_ON_ERROR,
|
||||||
|
lock_file_timeout_ms);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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) &&
|
||||||
|
launchctl_list_contains_plist(name, cmd))
|
||||||
|
rollback_lock_file(&lk);
|
||||||
|
else {
|
||||||
|
if (write_in_full(fd, plist.buf, plist.len) < 0 ||
|
||||||
|
commit_lock_file(&lk))
|
||||||
|
die_errno(_("could not write '%s'"), filename);
|
||||||
|
|
||||||
|
/* bootout might fail if not already running, so ignore */
|
||||||
|
launchctl_boot_plist(0, filename, cmd);
|
||||||
|
if (launchctl_boot_plist(1, filename, cmd))
|
||||||
|
die(_("failed to bootstrap service %s"), filename);
|
||||||
|
}
|
||||||
|
|
||||||
free(filename);
|
free(filename);
|
||||||
free(name);
|
free(name);
|
||||||
|
strbuf_release(&plist);
|
||||||
|
strbuf_release(&plist2);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -582,6 +582,23 @@ test_expect_success 'start and stop macOS maintenance' '
|
||||||
test_line_count = 0 actual
|
test_line_count = 0 actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'use launchctl list to prevent extra work' '
|
||||||
|
# ensure we are registered
|
||||||
|
GIT_TEST_MAINT_SCHEDULER=launchctl:./print-args git maintenance start &&
|
||||||
|
|
||||||
|
# do it again on a fresh args file
|
||||||
|
rm -f args &&
|
||||||
|
GIT_TEST_MAINT_SCHEDULER=launchctl:./print-args git maintenance start &&
|
||||||
|
|
||||||
|
ls "$HOME/Library/LaunchAgents" >actual &&
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
list org.git-scm.git.hourly
|
||||||
|
list org.git-scm.git.daily
|
||||||
|
list org.git-scm.git.weekly
|
||||||
|
EOF
|
||||||
|
test_cmp expect args
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'start and stop Windows maintenance' '
|
test_expect_success 'start and stop Windows maintenance' '
|
||||||
write_script print-args <<-\EOF &&
|
write_script print-args <<-\EOF &&
|
||||||
echo $* >>args
|
echo $* >>args
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue