31
#include "refs.h"
32
#include "remote.h"
33
#include "object-store.h"
34
+#include "exec-cmd.h"
35
36
#define FAILED_RUN "failed to run %s"
37
1457
return run_command(&config_unset);
1458
}
1459
1460
+#define BEGIN_LINE "# BEGIN GIT MAINTENANCE SCHEDULE"
1461
+#define END_LINE "# END GIT MAINTENANCE SCHEDULE"
1462
+
1463
+static int update_background_schedule(int run_maintenance)
1464
+{
1465
+ int result = 0;
1466
+ int in_old_region = 0;
1467
+ struct child_process crontab_list = CHILD_PROCESS_INIT;
1468
+ struct child_process crontab_edit = CHILD_PROCESS_INIT;
1469
+ FILE *cron_list, *cron_in;
1470
+ const char *crontab_name;
1471
+ struct strbuf line = STRBUF_INIT;
1472
+ struct lock_file lk;
1473
+ char *lock_path = xstrfmt("%s/schedule", the_repository->objects->odb->path);
1474
+
1475
+ if (hold_lock_file_for_update(&lk, lock_path, LOCK_NO_DEREF) < 0)
1476
+ return error(_("another process is scheduling background maintenance"));
1477
+
1478
+ crontab_name = getenv("GIT_TEST_CRONTAB");
1479
+ if (!crontab_name)
1480
+ crontab_name = "crontab";
1481
+
1482
+ strvec_split(&crontab_list.args, crontab_name);
1483
+ strvec_push(&crontab_list.args, "-l");
1484
+ crontab_list.in = -1;
1485
+ crontab_list.out = dup(lk.tempfile->fd);
1486
+ crontab_list.git_cmd = 0;
1487
+
1488
+ if (start_command(&crontab_list)) {
1489
+ result = error(_("failed to run 'crontab -l'; your system might not support 'cron'"));
1490
+ goto cleanup;
1491
+ }
1492
+
1493
+ /* Ignore exit code, as an empty crontab will return error. */
1494
+ finish_command(&crontab_list);
1495
+
1496
+ /*
1497
+ * Read from the .lock file, filtering out the old
1498
+ * schedule while appending the new schedule.
1499
+ */
1500
+ cron_list = fdopen(lk.tempfile->fd, "r");
1501
+ rewind(cron_list);
1502
+
1503
+ strvec_split(&crontab_edit.args, crontab_name);
1504
+ crontab_edit.in = -1;
1505
+ crontab_edit.git_cmd = 0;
1506
+
1507
+ if (start_command(&crontab_edit)) {
1508
+ result = error(_("failed to run 'crontab'; your system might not support 'cron'"));
1509
+ goto cleanup;
1510
+ }
1511
+
1512
+ cron_in = fdopen(crontab_edit.in, "w");
1513
+ if (!cron_in) {
1514
+ result = error(_("failed to open stdin of 'crontab'"));
1515
+ goto done_editing;
1516
+ }
1517
+
1518
+ while (!strbuf_getline_lf(&line, cron_list)) {
1519
+ if (!in_old_region && !strcmp(line.buf, BEGIN_LINE))
1520
+ in_old_region = 1;
1521
+ if (in_old_region)
1522
+ continue;
1523
+ fprintf(cron_in, "%s\n", line.buf);
1524
+ if (in_old_region && !strcmp(line.buf, END_LINE))
1525
+ in_old_region = 0;
1526
+ }
1527
+
1528
+ if (run_maintenance) {
1529
+ struct strbuf line_format = STRBUF_INIT;
1530
+ const char *exec_path = git_exec_path();
1531
+
1532
+ fprintf(cron_in, "%s\n", BEGIN_LINE);
1533
+ fprintf(cron_in,
1534
+ "# The following schedule was created by Git\n");
1535
+ fprintf(cron_in, "# Any edits made in this region might be\n");
1536
+ fprintf(cron_in,
1537
+ "# replaced in the future by a Git command.\n\n");
1538
+
1539
+ strbuf_addf(&line_format,
1540
+ "%%s %%s * * %%s \"%s/git\" --exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%%s\n",
1541
+ exec_path, exec_path);
1542
+ fprintf(cron_in, line_format.buf, "0", "1-23", "*", "hourly");
1543
+ fprintf(cron_in, line_format.buf, "0", "0", "1-6", "daily");
1544
+ fprintf(cron_in, line_format.buf, "0", "0", "0", "weekly");
1545
+ strbuf_release(&line_format);
1546
+
1547
+ fprintf(cron_in, "\n%s\n", END_LINE);
1548
+ }
1549
+
1550
+ fflush(cron_in);
1551
+ fclose(cron_in);
1552
+ close(crontab_edit.in);
1553
+
1554
+done_editing:
1555
+ if (finish_command(&crontab_edit)) {
1556
+ result = error(_("'crontab' died"));
1557
+ goto cleanup;
1558
+ }
1559
+ fclose(cron_list);
1560
+
1561
+cleanup:
1562
+ rollback_lock_file(&lk);
1563
+ return result;
1564
+}
1565
+
1566
+static int maintenance_start(void)
1567
+{
1568
+ if (maintenance_register())
1569
+ warning(_("failed to add repo to global config"));
1570
+
1571
+ return update_background_schedule(1);
1572
+}
1573
+
1574
+static int maintenance_stop(void)
1575
+{
1576
+ return update_background_schedule(0);
1577
+}
1578
+
1579
static const char builtin_maintenance_usage[] = N_("git maintenance <subcommand> [<options>]");
1580
1581
int cmd_maintenance(int argc, const char **argv, const char *prefix)
1586
1587
if (!strcmp(argv[1], "run"))
1588
return maintenance_run(argc - 1, argv + 1, prefix);
1589
+ if (!strcmp(argv[1], "start"))
1590
+ return maintenance_start();
1591
+ if (!strcmp(argv[1], "stop"))
1592
+ return maintenance_stop();
1593
if (!strcmp(argv[1], "register"))
1594
return maintenance_register();
1595
if (!strcmp(argv[1], "unregister"))