diff mbox series

[v4,1/3] config: rename `git_etc_config()`

Message ID 34bdbc27d618d7467d2caf6844d8c06bdcb8545b.1618297711.git.ps@pks.im (mailing list archive)
State Superseded
Commit 1b3d1f8f72dbef73f7d667ccc8f2e171736ca043
Headers show
Series config: allow overriding global/system config | expand

Commit Message

Patrick Steinhardt April 13, 2021, 7:11 a.m. UTC
The `git_etc_gitconfig()` function retrieves the system-level path of
the configuration file. We're about to introduce a way to override it
via an environment variable, at which point the name of this function
would start to become misleading.

Rename the function to `git_system_config()` as a preparatory step.
While at it, the function is also refactored to pass memory ownership to
the caller. This is done to better match semantics of
`git_global_config()`, which is going to be introduced in the next
commit.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/config.c |  2 +-
 config.c         | 18 ++++++++----------
 config.h         |  3 ++-
 3 files changed, 11 insertions(+), 12 deletions(-)

Comments

Jeff King April 13, 2021, 7:25 a.m. UTC | #1
On Tue, Apr 13, 2021 at 09:11:44AM +0200, Patrick Steinhardt wrote:

> While at it, the function is also refactored to pass memory ownership to
> the caller. This is done to better match semantics of
> `git_global_config()`, which is going to be introduced in the next
> commit.

I think this turned out nicely. There are only two callers, one of which
is already handling freeing the global config. In the other:

> diff --git a/builtin/config.c b/builtin/config.c
> index f71fa39b38..02ed0b3fe7 100644
> --- a/builtin/config.c
> +++ b/builtin/config.c
> @@ -695,7 +695,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
>  		}
>  	}
>  	else if (use_system_config) {
> -		given_config_source.file = git_etc_gitconfig();
> +		given_config_source.file = git_system_config();
>  		given_config_source.scope = CONFIG_SCOPE_SYSTEM;
>  	} else if (use_local_config) {
>  		given_config_source.file = git_pathdup("config");

We "leak" the result, but I think it is actually an improvement. As you
can see in the context, we are sometimes allocating the field in other
code paths, so this is takes us closer to a world where we can actually
call free(given_config_source.file) to fix the leak in all of the other
code paths. :)

(I don't think any of that needs to be dealt with in this series, of
course).

-Peff
SZEDER Gábor April 16, 2021, 9:14 p.m. UTC | #2
On Tue, Apr 13, 2021 at 09:11:44AM +0200, Patrick Steinhardt wrote:
> The `git_etc_gitconfig()` function retrieves the system-level path of
> the configuration file. We're about to introduce a way to override it
> via an environment variable, at which point the name of this function
> would start to become misleading.
> 
> Rename the function to `git_system_config()` as a preparatory step.
> While at it, the function is also refactored to pass memory ownership to
> the caller. This is done to better match semantics of
> `git_global_config()`, which is going to be introduced in the next
> commit.
> 
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
>  builtin/config.c |  2 +-
>  config.c         | 18 ++++++++----------
>  config.h         |  3 ++-
>  3 files changed, 11 insertions(+), 12 deletions(-)
> 
> diff --git a/builtin/config.c b/builtin/config.c
> index f71fa39b38..02ed0b3fe7 100644
> --- a/builtin/config.c
> +++ b/builtin/config.c
> @@ -695,7 +695,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
>  		}
>  	}
>  	else if (use_system_config) {
> -		given_config_source.file = git_etc_gitconfig();
> +		given_config_source.file = git_system_config();
>  		given_config_source.scope = CONFIG_SCOPE_SYSTEM;
>  	} else if (use_local_config) {
>  		given_config_source.file = git_pathdup("config");
> diff --git a/config.c b/config.c
> index 6428393a41..8c83669cce 100644
> --- a/config.c
> +++ b/config.c
> @@ -1844,12 +1844,9 @@ static int git_config_from_blob_ref(config_fn_t fn,
>  	return git_config_from_blob_oid(fn, name, &oid, data);
>  }
>  
> -const char *git_etc_gitconfig(void)
> +char *git_system_config(void)
>  {
> -	static const char *system_wide;
> -	if (!system_wide)
> -		system_wide = system_path(ETC_GITCONFIG);
> -	return system_wide;
> +	return system_path(ETC_GITCONFIG);
>  }
>  
>  /*
> @@ -1883,6 +1880,7 @@ static int do_git_config_sequence(const struct config_options *opts,
>  				  config_fn_t fn, void *data)
>  {
>  	int ret = 0;
> +	char *system_config = git_system_config();
>  	char *xdg_config = xdg_config_home("config");
>  	char *user_config = expand_user_path("~/.gitconfig", 0);
>  	char *repo_config;
> @@ -1896,11 +1894,10 @@ static int do_git_config_sequence(const struct config_options *opts,
>  		repo_config = NULL;
>  
>  	current_parsing_scope = CONFIG_SCOPE_SYSTEM;
> -	if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK,

Removing git_config_system() from the condition breaks
GIT_CONFIG_NOSYSTEM:

  expecting success of 9999.1 'test': 
  	cat /usr/local/etc/gitconfig &&
  	git config --list --show-origin --show-scope
  
  + cat /usr/local/etc/gitconfig
  [foo]
  	bar = baz
  + git config --list --show-origin --show-scope
  system	file:/usr/local/etc/gitconfig	foo.bar=baz
  local	file:.git/config	core.repositoryformatversion=0
  local	file:.git/config	core.filemode=true
  local	file:.git/config	core.bare=false
  local	file:.git/config	core.logallrefupdates=true
  ok 1 - test

And breaks just about everything the Linux32 job on Travis CI:

  https://travis-ci.org/github/git/git/jobs/767207687#L1218

> -						  opts->system_gently ?
> -						  ACCESS_EACCES_OK : 0))
> -		ret += git_config_from_file(fn, git_etc_gitconfig(),
> -					    data);
> +	if (system_config && !access_or_die(system_config, R_OK,
> +					    opts->system_gently ?
> +					    ACCESS_EACCES_OK : 0))
> +		ret += git_config_from_file(fn, system_config, data);
>  
>  	current_parsing_scope = CONFIG_SCOPE_GLOBAL;
>  	if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
> @@ -1927,6 +1924,7 @@ static int do_git_config_sequence(const struct config_options *opts,
>  		die(_("unable to parse command-line config"));
>  
>  	current_parsing_scope = prev_parsing_scope;
> +	free(system_config);
>  	free(xdg_config);
>  	free(user_config);
>  	free(repo_config);
> diff --git a/config.h b/config.h
> index 19a9adbaa9..2be8fa1880 100644
> --- a/config.h
> +++ b/config.h
> @@ -318,7 +318,6 @@ int git_config_rename_section(const char *, const char *);
>  int git_config_rename_section_in_file(const char *, const char *, const char *);
>  int git_config_copy_section(const char *, const char *);
>  int git_config_copy_section_in_file(const char *, const char *, const char *);
> -const char *git_etc_gitconfig(void);
>  int git_env_bool(const char *, int);
>  unsigned long git_env_ulong(const char *, unsigned long);
>  int git_config_system(void);
> @@ -327,6 +326,8 @@ int config_error_nonbool(const char *);
>  #define config_error_nonbool(s) (config_error_nonbool(s), const_error())
>  #endif
>  
> +char *git_system_config(void);
> +
>  int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
>  
>  enum config_scope current_config_scope(void);
> -- 
> 2.31.1
>
Jeff King April 17, 2021, 8:44 a.m. UTC | #3
On Fri, Apr 16, 2021 at 11:14:51PM +0200, SZEDER Gábor wrote:

> > @@ -1883,6 +1880,7 @@ static int do_git_config_sequence(const struct config_options *opts,
> >  				  config_fn_t fn, void *data)
> >  {
> >  	int ret = 0;
> > +	char *system_config = git_system_config();
> >  	char *xdg_config = xdg_config_home("config");
> >  	char *user_config = expand_user_path("~/.gitconfig", 0);
> >  	char *repo_config;
> > @@ -1896,11 +1894,10 @@ static int do_git_config_sequence(const struct config_options *opts,
> >  		repo_config = NULL;
> >  
> >  	current_parsing_scope = CONFIG_SCOPE_SYSTEM;
> > -	if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK,
> 
> Removing git_config_system() from the condition breaks
> GIT_CONFIG_NOSYSTEM:

Good catch. My gut feeling is that the new git_system_config() should
check NOSYSTEM and return NULL if it's set, and then we can get rid of
git_config_system() entirely.

That is slightly different than the old behavior; right now
GIT_CONFIG_NOSYSTEM only prevents reading during the normal sequence,
and not reading (or writing!) via "git config --system". But I think it
would be an improvement to prevent those (the whole point of the feature
was to avoid the test suite accidentally accessing the larger
environment).

-Peff
Junio C Hamano April 17, 2021, 9:37 p.m. UTC | #4
Jeff King <peff@peff.net> writes:

> On Fri, Apr 16, 2021 at 11:14:51PM +0200, SZEDER Gábor wrote:
>
>> > @@ -1883,6 +1880,7 @@ static int do_git_config_sequence(const struct config_options *opts,
>> >  				  config_fn_t fn, void *data)
>> >  {
>> >  	int ret = 0;
>> > +	char *system_config = git_system_config();
>> >  	char *xdg_config = xdg_config_home("config");
>> >  	char *user_config = expand_user_path("~/.gitconfig", 0);
>> >  	char *repo_config;
>> > @@ -1896,11 +1894,10 @@ static int do_git_config_sequence(const struct config_options *opts,
>> >  		repo_config = NULL;
>> >  
>> >  	current_parsing_scope = CONFIG_SCOPE_SYSTEM;
>> > -	if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK,
>> 
>> Removing git_config_system() from the condition breaks
>> GIT_CONFIG_NOSYSTEM:
>
> Good catch. My gut feeling is that the new git_system_config() should
> check NOSYSTEM and return NULL if it's set, and then we can get rid of
> git_config_system() entirely.

NULL -> /dev/null I guess?

> That is slightly different than the old behavior; right now
> GIT_CONFIG_NOSYSTEM only prevents reading during the normal sequence,
> and not reading (or writing!) via "git config --system". But I think it
> would be an improvement to prevent those (the whole point of the feature
> was to avoid the test suite accidentally accessing the larger
> environment).

Yup.
Jeff King April 18, 2021, 5:39 a.m. UTC | #5
On Sat, Apr 17, 2021 at 02:37:39PM -0700, Junio C Hamano wrote:

> Jeff King <peff@peff.net> writes:
> 
> > On Fri, Apr 16, 2021 at 11:14:51PM +0200, SZEDER Gábor wrote:
> >
> >> > @@ -1883,6 +1880,7 @@ static int do_git_config_sequence(const struct config_options *opts,
> >> >  				  config_fn_t fn, void *data)
> >> >  {
> >> >  	int ret = 0;
> >> > +	char *system_config = git_system_config();
> >> >  	char *xdg_config = xdg_config_home("config");
> >> >  	char *user_config = expand_user_path("~/.gitconfig", 0);
> >> >  	char *repo_config;
> >> > @@ -1896,11 +1894,10 @@ static int do_git_config_sequence(const struct config_options *opts,
> >> >  		repo_config = NULL;
> >> >  
> >> >  	current_parsing_scope = CONFIG_SCOPE_SYSTEM;
> >> > -	if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK,
> >> 
> >> Removing git_config_system() from the condition breaks
> >> GIT_CONFIG_NOSYSTEM:
> >
> > Good catch. My gut feeling is that the new git_system_config() should
> > check NOSYSTEM and return NULL if it's set, and then we can get rid of
> > git_config_system() entirely.
> 
> NULL -> /dev/null I guess?

I was thinking NULL to catch this line from the post-image of Patrick's
series:

          if (system_config && !access_or_die(system_config, R_OK,
                                              opts->system_gently ?
                                              ACCESS_EACCES_OK : 0))
                  ret += git_config_from_file(fn, system_config, data);

which would see that we have no file at all. But that may be unexpected
for other callers (right now git_etc_gitconfig() can never return NULL,
and I'm not sure what would happen with "git config --system"; I suspect
it would do the regular config sequence instead, which is wrong).

So yeah, probably returning /dev/null is more sensible (and makes it a
true alias for GIT_CONFIG_SYSTEM=/dev/null).

-Peff
Patrick Steinhardt April 19, 2021, 11:03 a.m. UTC | #6
On Sun, Apr 18, 2021 at 01:39:31AM -0400, Jeff King wrote:
> On Sat, Apr 17, 2021 at 02:37:39PM -0700, Junio C Hamano wrote:
> 
> > Jeff King <peff@peff.net> writes:
> > 
> > > On Fri, Apr 16, 2021 at 11:14:51PM +0200, SZEDER Gábor wrote:
> > >
> > >> > @@ -1883,6 +1880,7 @@ static int do_git_config_sequence(const struct config_options *opts,
> > >> >  				  config_fn_t fn, void *data)
> > >> >  {
> > >> >  	int ret = 0;
> > >> > +	char *system_config = git_system_config();
> > >> >  	char *xdg_config = xdg_config_home("config");
> > >> >  	char *user_config = expand_user_path("~/.gitconfig", 0);
> > >> >  	char *repo_config;
> > >> > @@ -1896,11 +1894,10 @@ static int do_git_config_sequence(const struct config_options *opts,
> > >> >  		repo_config = NULL;
> > >> >  
> > >> >  	current_parsing_scope = CONFIG_SCOPE_SYSTEM;
> > >> > -	if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK,
> > >> 
> > >> Removing git_config_system() from the condition breaks
> > >> GIT_CONFIG_NOSYSTEM:
> > >
> > > Good catch. My gut feeling is that the new git_system_config() should
> > > check NOSYSTEM and return NULL if it's set, and then we can get rid of
> > > git_config_system() entirely.
> > 
> > NULL -> /dev/null I guess?
> 
> I was thinking NULL to catch this line from the post-image of Patrick's
> series:
> 
>           if (system_config && !access_or_die(system_config, R_OK,
>                                               opts->system_gently ?
>                                               ACCESS_EACCES_OK : 0))
>                   ret += git_config_from_file(fn, system_config, data);
> 
> which would see that we have no file at all. But that may be unexpected
> for other callers (right now git_etc_gitconfig() can never return NULL,
> and I'm not sure what would happen with "git config --system"; I suspect
> it would do the regular config sequence instead, which is wrong).
> 
> So yeah, probably returning /dev/null is more sensible (and makes it a
> true alias for GIT_CONFIG_SYSTEM=/dev/null).
> 
> -Peff

It's only by accident that I dropped the call to `git_config_system()`,
must've happened when resolving conflicts somewhere. The issue with just
returning `/dev/null` from `git_system_config()` is that git-config(1)
would be broken, as you hint at. We do not honor GIT_CONFIG_NOSYSTEM
there if the "--system" flag was given.

So yes, we could change it to return `/dev/null`, but that would change
semantics of GIT_CONFIG_NOSYSTEM. I'm not sure doing this in the same
series is a good idea. Even more so because with returning `/dev/null`,
the conversion would be silent: whereas previous versions simply wrote
to or read from the system-level config, we now pretend to have read or
written something even though we didn't.

Patrick
Jeff King April 23, 2021, 9:27 a.m. UTC | #7
On Mon, Apr 19, 2021 at 01:03:56PM +0200, Patrick Steinhardt wrote:

> It's only by accident that I dropped the call to `git_config_system()`,
> must've happened when resolving conflicts somewhere. The issue with just
> returning `/dev/null` from `git_system_config()` is that git-config(1)
> would be broken, as you hint at. We do not honor GIT_CONFIG_NOSYSTEM
> there if the "--system" flag was given.
> 
> So yes, we could change it to return `/dev/null`, but that would change
> semantics of GIT_CONFIG_NOSYSTEM. I'm not sure doing this in the same
> series is a good idea. Even more so because with returning `/dev/null`,
> the conversion would be silent: whereas previous versions simply wrote
> to or read from the system-level config, we now pretend to have read or
> written something even though we didn't.

I'm OK being more cautious here, and leaving NOSYSTEM as it is.

My original thinking was that returning /dev/null would be an
improvement even for the existing call in "git config --system". And I
do think it would be for reading. But it actually gets pretty weird for
writing, because of our atomic-rename strategy.

And that is actually true of your new variable too. Doing this:

  GIT_CONFIG_SYSTEM=/dev/null git config --system foo.bar

would return nothing, which makes sense to me. But this:

  GIT_CONFIG_SYSTEM=/dev/null git config --system foo.bar value

would try to write /dev/null.lock, and then rename it into place over
the real /dev/null! That may be slightly surprising, but it is not
really any different than any other non-regular-file entry.

The stakes are slightly higher here, as it breaks all sorts of things
that can be hard to recover from (even if you know the correct mknod
incantation, important things like bash seem to get crabby if they can't
open /dev/null for writing). And it may be more likely to happen by
mistake (as opposed to "git config --file=/dev/null", which behaves the
same). But unless you are root, you are likely to just get an error in
the first place, so it seems like an unlikely mistake in practice.

So I think it may fall into "if it hurts, don't do it".

-Peff
diff mbox series

Patch

diff --git a/builtin/config.c b/builtin/config.c
index f71fa39b38..02ed0b3fe7 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -695,7 +695,7 @@  int cmd_config(int argc, const char **argv, const char *prefix)
 		}
 	}
 	else if (use_system_config) {
-		given_config_source.file = git_etc_gitconfig();
+		given_config_source.file = git_system_config();
 		given_config_source.scope = CONFIG_SCOPE_SYSTEM;
 	} else if (use_local_config) {
 		given_config_source.file = git_pathdup("config");
diff --git a/config.c b/config.c
index 6428393a41..8c83669cce 100644
--- a/config.c
+++ b/config.c
@@ -1844,12 +1844,9 @@  static int git_config_from_blob_ref(config_fn_t fn,
 	return git_config_from_blob_oid(fn, name, &oid, data);
 }
 
-const char *git_etc_gitconfig(void)
+char *git_system_config(void)
 {
-	static const char *system_wide;
-	if (!system_wide)
-		system_wide = system_path(ETC_GITCONFIG);
-	return system_wide;
+	return system_path(ETC_GITCONFIG);
 }
 
 /*
@@ -1883,6 +1880,7 @@  static int do_git_config_sequence(const struct config_options *opts,
 				  config_fn_t fn, void *data)
 {
 	int ret = 0;
+	char *system_config = git_system_config();
 	char *xdg_config = xdg_config_home("config");
 	char *user_config = expand_user_path("~/.gitconfig", 0);
 	char *repo_config;
@@ -1896,11 +1894,10 @@  static int do_git_config_sequence(const struct config_options *opts,
 		repo_config = NULL;
 
 	current_parsing_scope = CONFIG_SCOPE_SYSTEM;
-	if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK,
-						  opts->system_gently ?
-						  ACCESS_EACCES_OK : 0))
-		ret += git_config_from_file(fn, git_etc_gitconfig(),
-					    data);
+	if (system_config && !access_or_die(system_config, R_OK,
+					    opts->system_gently ?
+					    ACCESS_EACCES_OK : 0))
+		ret += git_config_from_file(fn, system_config, data);
 
 	current_parsing_scope = CONFIG_SCOPE_GLOBAL;
 	if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
@@ -1927,6 +1924,7 @@  static int do_git_config_sequence(const struct config_options *opts,
 		die(_("unable to parse command-line config"));
 
 	current_parsing_scope = prev_parsing_scope;
+	free(system_config);
 	free(xdg_config);
 	free(user_config);
 	free(repo_config);
diff --git a/config.h b/config.h
index 19a9adbaa9..2be8fa1880 100644
--- a/config.h
+++ b/config.h
@@ -318,7 +318,6 @@  int git_config_rename_section(const char *, const char *);
 int git_config_rename_section_in_file(const char *, const char *, const char *);
 int git_config_copy_section(const char *, const char *);
 int git_config_copy_section_in_file(const char *, const char *, const char *);
-const char *git_etc_gitconfig(void);
 int git_env_bool(const char *, int);
 unsigned long git_env_ulong(const char *, unsigned long);
 int git_config_system(void);
@@ -327,6 +326,8 @@  int config_error_nonbool(const char *);
 #define config_error_nonbool(s) (config_error_nonbool(s), const_error())
 #endif
 
+char *git_system_config(void);
+
 int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
 
 enum config_scope current_config_scope(void);