mirror of
https://git.code.sf.net/p/isync/isync
synced 2025-12-11 20:37:54 +01:00
Add IncludeCmd directive to config parser
Sometimes, UserCmd & PassCmd are not enough. While it would be possible to call mbsync with a completely generated config file, that seems mildly inelegant due to requiring a wrapper. On the downside, making other options' values available to the executed command would require some kind of "expando" feature. Complex shell commands are better delegated to scripts, so we do not add support for multi-line commands or try to avoid the need for multiple levels of quoting/escaping. A more conventional name for the directive would be "Eval", but it seems preferable to be consistent with the existing *Cmd options. An "Include" directive might be added later. This patch has been fixed up somewhat by the maintainer.
This commit is contained in:
committed by
Oswald Buddenhagen
parent
5fe30f5e8f
commit
aeac8e47d0
3
AUTHORS
3
AUTHORS
@@ -61,6 +61,9 @@ Oliver Runge <oliver.runge@gmail.com>
|
||||
Georgy Kibardin <georgy@kibardin.name>
|
||||
- Support for UTF-7 IMAP mailbox names
|
||||
|
||||
Michiel van den Heuvel <michielvdnheuvel@gmail.com>
|
||||
- IncludeCmd option
|
||||
|
||||
Honorary Contributors
|
||||
=====================
|
||||
|
||||
|
||||
8
NEWS
8
NEWS
@@ -1,3 +1,11 @@
|
||||
1.6.0 (TBD)
|
||||
==================
|
||||
|
||||
New Features:
|
||||
|
||||
- Improved support for dynamic configurations; option IncludeCmd
|
||||
|
||||
|
||||
1.5.1 (2025-03-11)
|
||||
==================
|
||||
|
||||
|
||||
98
src/config.c
98
src/config.c
@@ -61,12 +61,21 @@ expand_strdup( const char *s, const conffile_t *cfile )
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
conf_print_loc( const conffile_t *cfile )
|
||||
{
|
||||
if (cfile->eval_fp)
|
||||
fprintf( stderr, "%s:%d:included:%d: ", cfile->file, cfile->line, cfile->eval_line );
|
||||
else
|
||||
fprintf( stderr, "%s:%d: ", cfile->file, cfile->line );
|
||||
}
|
||||
|
||||
void
|
||||
conf_error( conffile_t *cfile, const char *fmt, ... )
|
||||
{
|
||||
va_list va;
|
||||
|
||||
fprintf( stderr, "%s:%d: ", cfile->file, cfile->line );
|
||||
conf_print_loc( cfile );
|
||||
va_start( va, fmt );
|
||||
vfprintf( stderr, fmt, va );
|
||||
va_end( va );
|
||||
@@ -79,7 +88,7 @@ conf_sys_error( conffile_t *cfile, const char *fmt, ... )
|
||||
va_list va;
|
||||
|
||||
int errno_bak = errno;
|
||||
fprintf( stderr, "%s:%d: ", cfile->file, cfile->line );
|
||||
conf_print_loc( cfile );
|
||||
errno = errno_bak;
|
||||
va_start( va, fmt );
|
||||
vsys_error( fmt, va );
|
||||
@@ -318,16 +327,78 @@ getopt_helper( conffile_t *cfile, int *cops, channel_conf_t *conf )
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
eval_cmd_popen( conffile_t *cfile, const char *cmd )
|
||||
{
|
||||
char *cd_cmd = xasprintf( "cd '%.*'s'; %s", cfile->path_len, cfile->file, cmd );
|
||||
|
||||
if (!(cfile->eval_fp = popen( cd_cmd, "r" ))) {
|
||||
sys_error( "popen" );
|
||||
cfile->err = 1;
|
||||
} else {
|
||||
cfile->eval_line = 0;
|
||||
cfile->eval_command = nfstrdup( cmd );
|
||||
}
|
||||
|
||||
free( cd_cmd );
|
||||
}
|
||||
|
||||
static void
|
||||
eval_cmd_pclose( conffile_t *cfile )
|
||||
{
|
||||
int ret = pclose( cfile->eval_fp );
|
||||
|
||||
// Do this here, so the exit code is not attributed to nested lines.
|
||||
cfile->eval_fp = NULL;
|
||||
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
sys_error( "pclose" );
|
||||
cfile->err = 1;
|
||||
} else if (WIFSIGNALED( ret )) {
|
||||
conf_error( cfile, "command \"%s\" crashed with signal %d\n",
|
||||
cfile->eval_command, WTERMSIG( ret ) );
|
||||
} else {
|
||||
conf_error( cfile, "command \"%s\" exited with status %d\n",
|
||||
cfile->eval_command, WEXITSTATUS( ret ) );
|
||||
}
|
||||
}
|
||||
|
||||
free( cfile->eval_command );
|
||||
cfile->eval_command = NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
read_cline( conffile_t *cfile )
|
||||
{
|
||||
if (cfile->eval_fp) {
|
||||
cfile->eval_line++;
|
||||
if ((cfile->rest = fgets( cfile->buf, cfile->bufl, cfile->eval_fp )) != NULL)
|
||||
return 1;
|
||||
eval_cmd_pclose( cfile );
|
||||
}
|
||||
cfile->line++;
|
||||
return (cfile->rest = fgets( cfile->buf, cfile->bufl, cfile->fp )) != NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
check_excess_tokens( conffile_t *cfile )
|
||||
{
|
||||
if (cfile->rest) {
|
||||
char *arg = get_arg( cfile, ARG_OPTIONAL, NULL );
|
||||
if (arg) {
|
||||
conf_error( cfile, "excess token '%s'\n", arg );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
getcline( conffile_t *cfile )
|
||||
{
|
||||
char *arg;
|
||||
|
||||
if (cfile->rest && (arg = get_arg( cfile, ARG_OPTIONAL, NULL )))
|
||||
conf_error( cfile, "excess token '%s'\n", arg );
|
||||
while (fgets( cfile->buf, cfile->bufl, cfile->fp )) {
|
||||
cfile->line++;
|
||||
cfile->rest = cfile->buf;
|
||||
check_excess_tokens( cfile );
|
||||
while (read_cline( cfile )) {
|
||||
int comment = 0;
|
||||
if (!(cfile->cmd = get_arg( cfile, ARG_OPTIONAL, &comment ))) {
|
||||
if (comment)
|
||||
@@ -336,6 +407,13 @@ getcline( conffile_t *cfile )
|
||||
}
|
||||
if (!(cfile->val = get_arg( cfile, ARG_REQUIRED, NULL )))
|
||||
continue;
|
||||
if (!strcasecmp( cfile->cmd, "IncludeCmd" )) {
|
||||
if (cfile->eval_fp)
|
||||
conf_error( cfile, "nested IncludeCmd\n" );
|
||||
else if (!check_excess_tokens( cfile ))
|
||||
eval_cmd_popen( cfile, cfile->val );
|
||||
continue;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -488,6 +566,7 @@ load_config( const char *where )
|
||||
return 1;
|
||||
}
|
||||
buf[sizeof(buf) - 1] = 0;
|
||||
cfile.eval_fp = NULL;
|
||||
cfile.buf = buf;
|
||||
cfile.bufl = sizeof(buf) - 1;
|
||||
cfile.line = 0;
|
||||
@@ -495,6 +574,7 @@ load_config( const char *where )
|
||||
cfile.ms_warn = 0;
|
||||
cfile.renew_warn = 0;
|
||||
cfile.delete_warn = 0;
|
||||
cfile.cmd = NULL;
|
||||
cfile.rest = NULL;
|
||||
|
||||
gcops = 0;
|
||||
|
||||
@@ -12,10 +12,13 @@
|
||||
|
||||
typedef struct {
|
||||
const char *file;
|
||||
char *eval_command;
|
||||
FILE *fp;
|
||||
FILE *eval_fp;
|
||||
char *buf;
|
||||
int bufl;
|
||||
int line;
|
||||
int eval_line;
|
||||
int err;
|
||||
int ms_warn, renew_warn, delete_warn;
|
||||
int path_len;
|
||||
|
||||
@@ -786,6 +786,15 @@ absolute limit, as even a single message can consume more memory than
|
||||
this.
|
||||
(Default: \fI10M\fR)
|
||||
.
|
||||
.SS Dynamic Configuration
|
||||
.TP
|
||||
\fBIncludeCmd\fR \fIcommand\fR
|
||||
Specify a shell command to obtain configuration file content from.
|
||||
The command is run in the configuration file's containing directory.
|
||||
This keyword can appear anywhere.
|
||||
The command's output will be interpreted as if it appeared inline;
|
||||
it can range from nothing at all to multiple sections.
|
||||
.
|
||||
.SH CONSOLE OUTPUT
|
||||
If \fBmbsync\fR's output is connected to a console, it will print progress
|
||||
counters by default. The output will look like this:
|
||||
|
||||
Reference in New Issue
Block a user