git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH] gc: use temporary file for editing crontab

From
Derrick Stolee <derrickstolee@github.com>
Date
Aug 23, 2022, 17:06 UTC
Message-ID
<9e737b4b-4a17-09d5-6452-4ca5eef3d9da@github.com>
In-Reply-To
<6428252p-ssrn-7qs7-9p26-5so10r96s3os@tzk.qr>
On 8/23/2022 5:12 AM, Johannes Schindelin wrote:
> Hi brian,
> 
> On Tue, 23 Aug 2022, brian m. carlson wrote:
Show 10 quoted lines
>> +	tmpedit = mks_tempfile_t(".git_cron_edit_tmpXXXXXX");
>> +	if (!tmpedit)
>> +		return error(_("failed to create crontab temporary file"));
> 
> It might make sense to use the same `goto out;` pattern here, to make it
> easier to reason about the early exit even six years from now.
> 
> We do not even have to guard the `close_tempfile_gently()` behind an `if
> (tempfile)` conditional because that function handles `NULL` parameters
> gently.
I don't think this is hard to reason about. It might mean that we
need to change this if block in the future to use 'goto out', if we
added another resource initialization before this one. That "future
need" is the only thing making me lean towards using the goto, but
we are just as likely to be in YAGNI territory here.
 
Show 39 quoted lines
>> +	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.
>> @@ -2086,19 +2096,6 @@ static int crontab_update_schedule(int run_maintenance, int fd)
>>  	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;
>> @@ -2132,14 +2129,22 @@ static int crontab_update_schedule(int run_maintenance, int fd)
>>  	}
>>
>>  	fflush(cron_in);
>> -	fclose(cron_in);
>> -	close(crontab_edit.in);
> 
> This worries me a bit. I could imagine that keeping the file open and then
> expecting a spawned process to read its stdin from that file won't work on
> Windows.

This is focused only on the cron integration, which is not used on Windows, so I'm not worried about that.

I was initially worried that we lost the fclose(cron_in), but of course it is handled by the close_tempfile_gently() at the end.

Show 15 quoted lines
> In any case, I would consider it the correct thing to do to close
> the temp file here. In other words, I would like to move the
> `close_tempfile_gently()` call to this location.
> 
>>
>> -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;
>> +	}
>> +

Here's the crux of the matter: we are no longer using stdin but instead passing an argument to point to a file with our desired schedule. I tested that this worked on my machine, and I'm glad this use is the POSIX standard.

There is something wrong with this patch: it needs to update t/helper/test-crontab.c in order to pass t7900-maintenance.sh.

Something like this works for me:
--- >8 ---
diff --git a/t/helper/test-crontab.c b/t/helper/test-crontab.c
index e7c0137a477..29425430466 100644
--- a/t/helper/test-crontab.c
+++ b/t/helper/test-crontab.c
@@ -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");

--- >8 ---

>>  	if (finish_command(&crontab_edit))
>>  		result = error(_("'crontab' died"));
>>  	else
>>  		fclose(cron_list);
>> +out:
>> +	close_tempfile_gently(tmpedit);
> 
> Here, I would like to call `delete_tempfile(&tmpedit);` instead. That way,
> the memory is released correctly, the temporary file is deleted, and
> everything is neatly cleaned up.
> 
> The way I read the code, `delete_tempfile(&tmpedit)` would return early if
> `tmpedit == NULL`, and otherwise clean everything up and release the
> memory, so there is no need to guard this call behind an `if (tmpedit)`
> conditional.

While the memory release is nice, I also think it would be good to use
delete_tempfile() so the temporary file is deleted within this method,
not waiting until the end of the process to do that cleanup.

Thanks,
-Stolee
Previous: Johannes SchindelinNext: brian m. carlson
Message 12 of 19 in “git maintenance broken on FreeBSD”
  1. Renato BotelhoAug 12, 2022
  2. Đoàn Trần Công DanhAug 12, 2022
  3. Todd ZullingerAug 13, 2022
  4. Junio C HamanoAug 13, 2022
  5. Đoàn Trần Công DanhAug 13, 2022
  6. Junio C HamanoAug 13, 2022
  7. brian m. carlsonAug 13, 2022
  8. Derrick StoleeAug 15, 2022
  9. Junio C HamanoAug 15, 2022
  10. gc: use temporary file for editing crontabbrian m. carlson, Aug 23, 2022
  11. Johannes SchindelinAug 23, 2022
  12. Derrick StoleeAug 23, 2022
  13. brian m. carlsonAug 23, 2022
  14. Junio C HamanoAug 24, 2022
  15. gc: use temporary file for editing crontabbrian m. carlson, Aug 28, 2022
  16. Junio C HamanoAug 29, 2022
  17. Renato BotelhoAug 29, 2022
  18. Derrick StoleeAug 30, 2022
  19. test-crontab: minor memory and error handling fixesJeff King, Aug 30, 2022

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.