Skip to content

Commit

Permalink
gc: use temporary file for editing crontab
Browse files Browse the repository at this point in the history
While cron is specified by POSIX, there are a wide variety of
implementations in use.  "git maintenance" assumes that the
"crontab" command can be fed from its standard input the new
contents and the syntax to do so is not to have any filename
argument, as POSIX describes.  However, on FreeBSD, the cron
implementation requires a file name argument: if the user wants to
edit standard input, they must specify "-".

Unfortunately, POSIX systems do not have to interpret "-" on the
command line of crontab as a request to read from the standard
input.  Blindly adding "-" on the command line would not work as a
general solution.

Since POSIX tells us that cron must accept a file name argument, let's
solve this problem by specifying a temporary file instead.  This will
ensure that we work with the vast majority of implementations.

Note that because delete_tempfile closes the file for us, we should not
call fclose here on the handle, since doing so will introduce a double
free.

Reported-by: Renato Botelho <[email protected]>
Signed-off-by: brian m. carlson <[email protected]>
Signed-off-by: Junio C Hamano <[email protected]>
  • Loading branch information
bk2204 authored and gitster committed Aug 28, 2022
1 parent 0f5bd02 commit ee69e78
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 18 deletions.
39 changes: 23 additions & 16 deletions builtin/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2059,6 +2059,7 @@ static int crontab_update_schedule(int run_maintenance, int fd)
struct child_process crontab_edit = CHILD_PROCESS_INIT;
FILE *cron_list, *cron_in;
struct strbuf line = STRBUF_INIT;
struct tempfile *tmpedit = NULL;

get_schedule_cmd(&cmd, NULL);
strvec_split(&crontab_list.args, cmd);
Expand All @@ -2073,26 +2074,24 @@ static int crontab_update_schedule(int run_maintenance, int fd)
/* Ignore exit code, as an empty crontab will return error. */
finish_command(&crontab_list);

tmpedit = mks_tempfile_t(".git_cron_edit_tmpXXXXXX");
if (!tmpedit) {
result = error(_("failed to create crontab temporary file"));
goto out;
}
cron_in = fdopen_tempfile(tmpedit, "w");
if (!cron_in) {
result = error(_("failed to open temporary file"));
goto out;
}

/*
* Read from the .lock file, filtering out the old
* schedule while appending the new schedule.
*/
cron_list = fdopen(fd, "r");
rewind(cron_list);

strvec_split(&crontab_edit.args, cmd);
crontab_edit.in = -1;
crontab_edit.git_cmd = 0;

if (start_command(&crontab_edit))
return error(_("failed to run 'crontab'; your system might not support 'cron'"));

cron_in = fdopen(crontab_edit.in, "w");
if (!cron_in) {
result = error(_("failed to open stdin of 'crontab'"));
goto done_editing;
}

while (!strbuf_getline_lf(&line, cron_list)) {
if (!in_old_region && !strcmp(line.buf, BEGIN_LINE))
in_old_region = 1;
Expand Down Expand Up @@ -2126,14 +2125,22 @@ static int crontab_update_schedule(int run_maintenance, int fd)
}

fflush(cron_in);
fclose(cron_in);
close(crontab_edit.in);

done_editing:
strvec_split(&crontab_edit.args, cmd);
strvec_push(&crontab_edit.args, get_tempfile_path(tmpedit));
crontab_edit.git_cmd = 0;

if (start_command(&crontab_edit)) {
result = error(_("failed to run 'crontab'; your system might not support 'cron'"));
goto out;
}

if (finish_command(&crontab_edit))
result = error(_("'crontab' died"));
else
fclose(cron_list);
out:
delete_tempfile(&tmpedit);
return result;
}

Expand Down
4 changes: 2 additions & 2 deletions t/helper/test-crontab.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ int cmd__crontab(int argc, const char **argv)
if (!from)
return 0;
to = stdout;
} else if (argc == 2) {
from = stdin;
} else if (argc == 3) {
from = fopen(argv[2], "r");
to = fopen(argv[1], "w");
} else
return error("unknown arguments");
Expand Down

0 comments on commit ee69e78

Please sign in to comment.