1589
die(_("failed to create directories for '%s'"), filename);
1590
plist = xfopen(filename, "w");
1591
1592
- preamble = "<?xml version=\"1.0\" encoding=\"US-ASCII\"?>\n"
1592
+ preamble = "<?xml version=\"1.0\"?>\n"
1593
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
1594
"<plist version=\"1.0\">"
1595
"<dict>\n"
1671
return launchctl_remove_plists(cmd);
1672
}
1673
1674
+static char *schtasks_task_name(const char *frequency)
1675
+{
1676
+ struct strbuf label = STRBUF_INIT;
1677
+ strbuf_addf(&label, "Git Maintenance (%s)", frequency);
1678
+ return strbuf_detach(&label, NULL);
1679
+}
1680
+
1681
+static int schtasks_remove_task(enum schedule_priority schedule, const char *cmd)
1682
+{
1683
+ int result;
1684
+ struct strvec args = STRVEC_INIT;
1685
+ const char *frequency = get_frequency(schedule);
1686
+ char *name = schtasks_task_name(frequency);
1687
+
1688
+ strvec_split(&args, cmd);
1689
+ strvec_pushl(&args, "/delete", "/tn", name, "/f", NULL);
1690
+
1691
+ result = run_command_v_opt(args.v, 0);
1692
+
1693
+ strvec_clear(&args);
1694
+ free(name);
1695
+ return result;
1696
+}
1697
+
1698
+static int schtasks_remove_tasks(const char *cmd)
1699
+{
1700
+ return schtasks_remove_task(SCHEDULE_HOURLY, cmd) ||
1701
+ schtasks_remove_task(SCHEDULE_DAILY, cmd) ||
1702
+ schtasks_remove_task(SCHEDULE_WEEKLY, cmd);
1703
+}
1704
+
1705
+static int schtasks_schedule_task(const char *exec_path, enum schedule_priority schedule, const char *cmd)
1706
+{
1707
+ int result;
1708
+ struct child_process child = CHILD_PROCESS_INIT;
1709
+ const char *xml;
1710
+ struct tempfile *tfile;
1711
+ const char *frequency = get_frequency(schedule);
1712
+ char *name = schtasks_task_name(frequency);
1713
+ struct strbuf tfilename = STRBUF_INIT;
1714
+
1715
+ strbuf_addf(&tfilename, "%s/schedule_%s_XXXXXX",
1716
+ get_git_common_dir(), frequency);
1717
+ tfile = xmks_tempfile(tfilename.buf);
1718
+ strbuf_release(&tfilename);
1719
+
1720
+ if (!fdopen_tempfile(tfile, "w"))
1721
+ die(_("failed to create temp xml file"));
1722
+
1723
+ xml = "<?xml version=\"1.0\" ?>\n"
1724
+ "<Task version=\"1.4\" xmlns=\"http://schemas.microsoft.com/windows/2004/02/mit/task\">\n"
1725
+ "<Triggers>\n"
1726
+ "<CalendarTrigger>\n";
1727
+ fputs(xml, tfile->fp);
1728
+
1729
+ switch (schedule) {
1730
+ case SCHEDULE_HOURLY:
1731
+ fprintf(tfile->fp,
1732
+ "<StartBoundary>2020-01-01T01:00:00</StartBoundary>\n"
1733
+ "<Enabled>true</Enabled>\n"
1734
+ "<ScheduleByDay>\n"
1735
+ "<DaysInterval>1</DaysInterval>\n"
1736
+ "</ScheduleByDay>\n"
1737
+ "<Repetition>\n"
1738
+ "<Interval>PT1H</Interval>\n"
1739
+ "<Duration>PT23H</Duration>\n"
1740
+ "<StopAtDurationEnd>false</StopAtDurationEnd>\n"
1741
+ "</Repetition>\n");
1742
+ break;
1743
+
1744
+ case SCHEDULE_DAILY:
1745
+ fprintf(tfile->fp,
1746
+ "<StartBoundary>2020-01-01T00:00:00</StartBoundary>\n"
1747
+ "<Enabled>true</Enabled>\n"
1748
+ "<ScheduleByWeek>\n"
1749
+ "<DaysOfWeek>\n"
1750
+ "<Monday />\n"
1751
+ "<Tuesday />\n"
1752
+ "<Wednesday />\n"
1753
+ "<Thursday />\n"
1754
+ "<Friday />\n"
1755
+ "<Saturday />\n"
1756
+ "</DaysOfWeek>\n"
1757
+ "<WeeksInterval>1</WeeksInterval>\n"
1758
+ "</ScheduleByWeek>\n");
1759
+ break;
1760
+
1761
+ case SCHEDULE_WEEKLY:
1762
+ fprintf(tfile->fp,
1763
+ "<StartBoundary>2020-01-01T00:00:00</StartBoundary>\n"
1764
+ "<Enabled>true</Enabled>\n"
1765
+ "<ScheduleByWeek>\n"
1766
+ "<DaysOfWeek>\n"
1767
+ "<Sunday />\n"
1768
+ "</DaysOfWeek>\n"
1769
+ "<WeeksInterval>1</WeeksInterval>\n"
1770
+ "</ScheduleByWeek>\n");
1771
+ break;
1772
+
1773
+ default:
1774
+ break;
1775
+ }
1776
+
1777
+ xml = "</CalendarTrigger>\n"
1778
+ "</Triggers>\n"
1779
+ "<Principals>\n"
1780
+ "<Principal id=\"Author\">\n"
1781
+ "<LogonType>InteractiveToken</LogonType>\n"
1782
+ "<RunLevel>LeastPrivilege</RunLevel>\n"
1783
+ "</Principal>\n"
1784
+ "</Principals>\n"
1785
+ "<Settings>\n"
1786
+ "<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>\n"
1787
+ "<Enabled>true</Enabled>\n"
1788
+ "<Hidden>true</Hidden>\n"
1789
+ "<UseUnifiedSchedulingEngine>true</UseUnifiedSchedulingEngine>\n"
1790
+ "<WakeToRun>false</WakeToRun>\n"
1791
+ "<ExecutionTimeLimit>PT72H</ExecutionTimeLimit>\n"
1792
+ "<Priority>7</Priority>\n"
1793
+ "</Settings>\n"
1794
+ "<Actions Context=\"Author\">\n"
1795
+ "<Exec>\n"
1796
+ "<Command>\"%s\\git.exe\"</Command>\n"
1797
+ "<Arguments>--exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%s</Arguments>\n"
1798
+ "</Exec>\n"
1799
+ "</Actions>\n"
1800
+ "</Task>\n";
1801
+ fprintf(tfile->fp, xml, exec_path, exec_path, frequency);
1802
+ strvec_split(&child.args, cmd);
1803
+ strvec_pushl(&child.args, "/create", "/tn", name, "/f", "/xml",
1804
+ get_tempfile_path(tfile), NULL);
1805
+ close_tempfile_gently(tfile);
1806
+
1807
+ child.no_stdout = 1;
1808
+ child.no_stderr = 1;
1809
+
1810
+ if (start_command(&child))
1811
+ die(_("failed to start schtasks"));
1812
+ result = finish_command(&child);
1813
+
1814
+ delete_tempfile(&tfile);
1815
+ free(name);
1816
+ return result;
1817
+}
1818
+
1819
+static int schtasks_schedule_tasks(const char *cmd)
1820
+{
1821
+ const char *exec_path = git_exec_path();
1822
+
1823
+ return schtasks_schedule_task(exec_path, SCHEDULE_HOURLY, cmd) ||
1824
+ schtasks_schedule_task(exec_path, SCHEDULE_DAILY, cmd) ||
1825
+ schtasks_schedule_task(exec_path, SCHEDULE_WEEKLY, cmd);
1826
+}
1827
+
1828
+static int schtasks_update_schedule(int run_maintenance, int fd, const char *cmd)
1829
+{
1830
+ if (run_maintenance)
1831
+ return schtasks_schedule_tasks(cmd);
1832
+ else
1833
+ return schtasks_remove_tasks(cmd);
1834
+}
1835
+
1836
#define BEGIN_LINE "# BEGIN GIT MAINTENANCE SCHEDULE"
1837
#define END_LINE "# END GIT MAINTENANCE SCHEDULE"
1838
1923
1924
#if defined(__APPLE__)
1925
static const char platform_scheduler[] = "launchctl";
1926
+#elif defined(GIT_WINDOWS_NATIVE)
1927
+static const char platform_scheduler[] = "schtasks";
1928
#else
1929
static const char platform_scheduler[] = "crontab";
1930
#endif
1953
1954
if (!strcmp(scheduler, "launchctl"))
1955
result = launchctl_update_schedule(enable, lk.tempfile->fd, cmd);
1956
+ else if (!strcmp(scheduler, "schtasks"))
1957
+ result = schtasks_update_schedule(enable, lk.tempfile->fd, cmd);
1958
else if (!strcmp(scheduler, "crontab"))
1959
result = crontab_update_schedule(enable, lk.tempfile->fd, cmd);
1960
else