mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
2073fd0956
Signed-off-by: Amitay Isaacs <amitay@gmail.com> Reviewed-by: Andreas Schneider <asn@samba.org> Autobuild-User(master): Andreas Schneider <asn@cryptomilk.org> Autobuild-Date(master): Tue May 8 12:55:04 CEST 2018 on sn-devel-144
583 lines
13 KiB
C
583 lines
13 KiB
C
/** \ingroup popt
|
|
* \file popt/poptconfig.c
|
|
*/
|
|
|
|
/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
|
|
file accompanying popt source distributions, available from
|
|
ftp://ftp.rpm.org/pub/rpm/dist. */
|
|
|
|
#include "system.h"
|
|
#include "poptint.h"
|
|
#include <sys/stat.h>
|
|
|
|
#if defined(HAVE_FNMATCH_H)
|
|
#include <fnmatch.h>
|
|
|
|
#if defined(__LCLINT__)
|
|
/*@-declundef -exportheader -incondefs -protoparammatch -redecl -type @*/
|
|
extern int fnmatch (const char *__pattern, const char *__name, int __flags)
|
|
/*@*/;
|
|
/*@=declundef =exportheader =incondefs =protoparammatch =redecl =type @*/
|
|
#endif /* __LCLINT__ */
|
|
#endif
|
|
|
|
#if defined(HAVE_GLOB_H)
|
|
#include <glob.h>
|
|
|
|
#if defined(__LCLINT__)
|
|
/*@-declundef -exportheader -incondefs -protoparammatch -redecl -type @*/
|
|
extern int glob (const char *__pattern, int __flags,
|
|
/*@null@*/ int (*__errfunc) (const char *, int),
|
|
/*@out@*/ glob_t *__pglob)
|
|
/*@globals errno, fileSystem @*/
|
|
/*@modifies *__pglob, errno, fileSystem @*/;
|
|
|
|
/* XXX only annotation is a white lie */
|
|
extern void globfree (/*@only@*/ glob_t *__pglob)
|
|
/*@modifies *__pglob @*/;
|
|
|
|
/* XXX _GNU_SOURCE ifdef and/or retrofit is needed for portability. */
|
|
extern int glob_pattern_p (const char *__pattern, int __quote)
|
|
/*@*/;
|
|
/*@=declundef =exportheader =incondefs =protoparammatch =redecl =type @*/
|
|
#endif /* __LCLINT__ */
|
|
|
|
#if !defined(__GLIBC__)
|
|
/* Return nonzero if PATTERN contains any metacharacters.
|
|
Metacharacters can be quoted with backslashes if QUOTE is nonzero. */
|
|
static int
|
|
glob_pattern_p (const char * pattern, int quote)
|
|
/*@*/
|
|
{
|
|
const char * p;
|
|
int open = 0;
|
|
|
|
for (p = pattern; *p != '\0'; ++p)
|
|
switch (*p) {
|
|
case '?':
|
|
case '*':
|
|
return 1;
|
|
/*@notreached@*/ /*@switchbreak@*/ break;
|
|
case '\\':
|
|
if (quote && p[1] != '\0')
|
|
++p;
|
|
/*@switchbreak@*/ break;
|
|
case '[':
|
|
open = 1;
|
|
/*@switchbreak@*/ break;
|
|
case ']':
|
|
if (open)
|
|
return 1;
|
|
/*@switchbreak@*/ break;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif /* !defined(__GLIBC__) */
|
|
|
|
/*@unchecked@*/
|
|
static int poptGlobFlags = 0;
|
|
|
|
static int poptGlob_error(/*@unused@*/ UNUSED(const char * epath),
|
|
/*@unused@*/ UNUSED(int eerrno))
|
|
/*@*/
|
|
{
|
|
return 1;
|
|
}
|
|
#endif /* HAVE_GLOB_H */
|
|
|
|
/**
|
|
* Return path(s) from a glob pattern.
|
|
* @param con context
|
|
* @param pattern glob pattern
|
|
* @retval *acp no. of paths
|
|
* @retval *avp array of paths
|
|
* @return 0 on success
|
|
*/
|
|
static int poptGlob(/*@unused@*/ UNUSED(poptContext con), const char * pattern,
|
|
/*@out@*/ int * acp, /*@out@*/ const char *** avp)
|
|
/*@modifies *acp, *avp @*/
|
|
{
|
|
const char * pat = pattern;
|
|
int rc = 0; /* assume success */
|
|
|
|
/* XXX skip the attention marker. */
|
|
if (pat[0] == '@' && pat[1] != '(')
|
|
pat++;
|
|
|
|
#if defined(HAVE_GLOB_H)
|
|
if (glob_pattern_p(pat, 0)) {
|
|
glob_t _g, *pglob = &_g;
|
|
|
|
if (!glob(pat, poptGlobFlags, poptGlob_error, pglob)) {
|
|
if (acp) {
|
|
*acp = (int) pglob->gl_pathc;
|
|
pglob->gl_pathc = 0;
|
|
}
|
|
if (avp) {
|
|
/*@-onlytrans@*/
|
|
*avp = (const char **) pglob->gl_pathv;
|
|
/*@=onlytrans@*/
|
|
pglob->gl_pathv = NULL;
|
|
}
|
|
/*@-nullstate@*/
|
|
globfree(pglob);
|
|
/*@=nullstate@*/
|
|
} else
|
|
rc = POPT_ERROR_ERRNO;
|
|
} else
|
|
#endif /* HAVE_GLOB_H */
|
|
{
|
|
if (acp)
|
|
*acp = 1;
|
|
if (avp && (*avp = calloc((size_t)(1 + 1), sizeof (**avp))) != NULL)
|
|
(*avp)[0] = xstrdup(pat);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*@access poptContext @*/
|
|
|
|
int poptSaneFile(const char * fn)
|
|
{
|
|
struct stat sb;
|
|
uid_t uid = getuid();
|
|
|
|
if (stat(fn, &sb) == -1)
|
|
return 1;
|
|
if ((uid_t)sb.st_uid != uid)
|
|
return 0;
|
|
if (!S_ISREG(sb.st_mode))
|
|
return 0;
|
|
/*@-bitwisesigned@*/
|
|
if (sb.st_mode & (S_IWGRP|S_IWOTH))
|
|
return 0;
|
|
/*@=bitwisesigned@*/
|
|
return 1;
|
|
}
|
|
|
|
int poptReadFile(const char * fn, char ** bp, size_t * nbp, int flags)
|
|
{
|
|
int fdno;
|
|
char * b = NULL;
|
|
off_t nb = 0;
|
|
char * s, * t, * se;
|
|
int rc = POPT_ERROR_ERRNO; /* assume failure */
|
|
|
|
fdno = open(fn, O_RDONLY);
|
|
if (fdno < 0)
|
|
goto exit;
|
|
|
|
if ((nb = lseek(fdno, 0, SEEK_END)) == (off_t)-1
|
|
|| lseek(fdno, 0, SEEK_SET) == (off_t)-1
|
|
|| (b = calloc(sizeof(*b), (size_t)nb + 1)) == NULL
|
|
|| read(fdno, (char *)b, (size_t)nb) != (ssize_t)nb)
|
|
{
|
|
int oerrno = errno;
|
|
(void) close(fdno);
|
|
errno = oerrno;
|
|
goto exit;
|
|
}
|
|
if (close(fdno) == -1)
|
|
goto exit;
|
|
if (b == NULL) {
|
|
rc = POPT_ERROR_MALLOC;
|
|
goto exit;
|
|
}
|
|
rc = 0;
|
|
|
|
/* Trim out escaped newlines. */
|
|
/*@-bitwisesigned@*/
|
|
if (flags & POPT_READFILE_TRIMNEWLINES)
|
|
/*@=bitwisesigned@*/
|
|
{
|
|
for (t = b, s = b, se = b + nb; *s && s < se; s++) {
|
|
switch (*s) {
|
|
case '\\':
|
|
if (s[1] == '\n') {
|
|
s++;
|
|
continue;
|
|
}
|
|
/*@fallthrough@*/
|
|
default:
|
|
*t++ = *s;
|
|
/*@switchbreak@*/ break;
|
|
}
|
|
}
|
|
*t++ = '\0';
|
|
nb = (off_t)(t - b);
|
|
}
|
|
|
|
exit:
|
|
if (rc != 0) {
|
|
/*@-usedef@*/
|
|
if (b)
|
|
free(b);
|
|
/*@=usedef@*/
|
|
b = NULL;
|
|
nb = 0;
|
|
}
|
|
if (bp)
|
|
*bp = b;
|
|
/*@-usereleased@*/
|
|
else if (b)
|
|
free(b);
|
|
/*@=usereleased@*/
|
|
if (nbp)
|
|
*nbp = (size_t)nb;
|
|
/*@-compdef -nullstate @*/ /* XXX cannot annotate char ** correctly */
|
|
return rc;
|
|
/*@=compdef =nullstate @*/
|
|
}
|
|
|
|
/**
|
|
* Check for application match.
|
|
* @param con context
|
|
* @param s config application name
|
|
* return 0 if config application matches
|
|
*/
|
|
static int configAppMatch(poptContext con, const char * s)
|
|
/*@*/
|
|
{
|
|
int rc = 1;
|
|
|
|
if (con->appName == NULL) /* XXX can't happen. */
|
|
return rc;
|
|
|
|
#if defined(HAVE_GLOB_H) && defined(HAVE_FNMATCH_H)
|
|
if (glob_pattern_p(s, 1)) {
|
|
/*@-bitwisesigned@*/
|
|
static int flags = FNM_PATHNAME | FNM_PERIOD;
|
|
#ifdef FNM_EXTMATCH
|
|
flags |= FNM_EXTMATCH;
|
|
#endif
|
|
/*@=bitwisesigned@*/
|
|
rc = fnmatch(s, con->appName, flags);
|
|
} else
|
|
#endif
|
|
rc = strcmp(s, con->appName);
|
|
return rc;
|
|
}
|
|
|
|
/*@-compmempass@*/ /* FIX: item->option.longName kept, not dependent. */
|
|
static int poptConfigLine(poptContext con, char * line)
|
|
/*@globals fileSystem, internalState @*/
|
|
/*@modifies con, fileSystem, internalState @*/
|
|
{
|
|
char *b = NULL;
|
|
size_t nb = 0;
|
|
char * se = line;
|
|
const char * appName;
|
|
const char * entryType;
|
|
const char * opt;
|
|
struct poptItem_s item_buf;
|
|
poptItem item = &item_buf;
|
|
int i, j;
|
|
int rc = POPT_ERROR_BADCONFIG;
|
|
|
|
if (con->appName == NULL)
|
|
goto exit;
|
|
|
|
memset(item, 0, sizeof(*item));
|
|
|
|
appName = se;
|
|
while (*se != '\0' && !_isspaceptr(se)) se++;
|
|
if (*se == '\0')
|
|
goto exit;
|
|
else
|
|
*se++ = '\0';
|
|
|
|
if (configAppMatch(con, appName)) goto exit;
|
|
|
|
while (*se != '\0' && _isspaceptr(se)) se++;
|
|
entryType = se;
|
|
while (*se != '\0' && !_isspaceptr(se)) se++;
|
|
if (*se != '\0') *se++ = '\0';
|
|
|
|
while (*se != '\0' && _isspaceptr(se)) se++;
|
|
if (*se == '\0') goto exit;
|
|
opt = se;
|
|
while (*se != '\0' && !_isspaceptr(se)) se++;
|
|
if (opt[0] == '-' && *se == '\0') goto exit;
|
|
if (*se != '\0') *se++ = '\0';
|
|
|
|
while (*se != '\0' && _isspaceptr(se)) se++;
|
|
if (opt[0] == '-' && *se == '\0') goto exit;
|
|
|
|
/*@-temptrans@*/ /* FIX: line alias is saved */
|
|
if (opt[0] == '-' && opt[1] == '-')
|
|
item->option.longName = opt + 2;
|
|
else if (opt[0] == '-' && opt[2] == '\0')
|
|
item->option.shortName = opt[1];
|
|
else {
|
|
const char * fn = opt;
|
|
|
|
/* XXX handle globs and directories in fn? */
|
|
if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0)
|
|
goto exit;
|
|
if (b == NULL || nb == 0)
|
|
goto exit;
|
|
|
|
/* Append remaining text to the interpolated file option text. */
|
|
if (*se != '\0') {
|
|
size_t nse = strlen(se) + 1;
|
|
if ((b = realloc(b, (nb + nse))) == NULL) /* XXX can't happen */
|
|
goto exit;
|
|
(void) stpcpy( stpcpy(&b[nb-1], " "), se);
|
|
nb += nse;
|
|
}
|
|
se = b;
|
|
|
|
/* Use the basename of the path as the long option name. */
|
|
{ const char * longName = strrchr(fn, '/');
|
|
if (longName != NULL)
|
|
longName++;
|
|
else
|
|
longName = fn;
|
|
if (longName == NULL) /* XXX can't happen. */
|
|
goto exit;
|
|
/* Single character basenames are treated as short options. */
|
|
if (longName[1] != '\0')
|
|
item->option.longName = longName;
|
|
else
|
|
item->option.shortName = longName[0];
|
|
}
|
|
}
|
|
/*@=temptrans@*/
|
|
|
|
if (poptParseArgvString(se, &item->argc, &item->argv)) goto exit;
|
|
|
|
/*@-modobserver@*/
|
|
item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN;
|
|
for (i = 0, j = 0; i < item->argc; i++, j++) {
|
|
const char * f;
|
|
if (!strncmp(item->argv[i], "--POPTdesc=", sizeof("--POPTdesc=")-1)) {
|
|
f = item->argv[i] + sizeof("--POPTdesc=");
|
|
if (f[0] == '$' && f[1] == '"') f++;
|
|
item->option.descrip = f;
|
|
item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
|
|
j--;
|
|
} else
|
|
if (!strncmp(item->argv[i], "--POPTargs=", sizeof("--POPTargs=")-1)) {
|
|
f = item->argv[i] + sizeof("--POPTargs=");
|
|
if (f[0] == '$' && f[1] == '"') f++;
|
|
item->option.argDescrip = f;
|
|
item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
|
|
item->option.argInfo |= POPT_ARG_STRING;
|
|
j--;
|
|
} else
|
|
if (j != i)
|
|
item->argv[j] = item->argv[i];
|
|
}
|
|
if (j != i) {
|
|
item->argv[j] = NULL;
|
|
item->argc = j;
|
|
}
|
|
/*@=modobserver@*/
|
|
|
|
/*@-nullstate@*/ /* FIX: item->argv[] may be NULL */
|
|
if (!strcmp(entryType, "alias"))
|
|
rc = poptAddItem(con, item, 0);
|
|
else if (!strcmp(entryType, "exec"))
|
|
rc = poptAddItem(con, item, 1);
|
|
/*@=nullstate@*/
|
|
exit:
|
|
rc = 0; /* XXX for now, always return success */
|
|
if (b)
|
|
free(b);
|
|
return rc;
|
|
}
|
|
/*@=compmempass@*/
|
|
|
|
int poptReadConfigFile(poptContext con, const char * fn)
|
|
{
|
|
char * b = NULL, *be;
|
|
size_t nb = 0;
|
|
const char *se;
|
|
char *t, *te;
|
|
int rc;
|
|
int xx;
|
|
|
|
if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0)
|
|
return (errno == ENOENT ? 0 : rc);
|
|
if (b == NULL || nb == 0)
|
|
return POPT_ERROR_BADCONFIG;
|
|
|
|
if ((t = malloc(nb + 1)) == NULL)
|
|
goto exit;
|
|
te = t;
|
|
|
|
be = (b + nb);
|
|
for (se = b; se < be; se++) {
|
|
switch (*se) {
|
|
case '\n':
|
|
*te = '\0';
|
|
te = t;
|
|
while (*te && _isspaceptr(te)) te++;
|
|
if (*te && *te != '#')
|
|
xx = poptConfigLine(con, te);
|
|
/*@switchbreak@*/ break;
|
|
/*@-usedef@*/ /* XXX *se may be uninitialized */
|
|
case '\\':
|
|
*te = *se++;
|
|
/* \ at the end of a line does not insert a \n */
|
|
if (se < be && *se != '\n') {
|
|
te++;
|
|
*te++ = *se;
|
|
}
|
|
/*@switchbreak@*/ break;
|
|
default:
|
|
*te++ = *se;
|
|
/*@switchbreak@*/ break;
|
|
/*@=usedef@*/
|
|
}
|
|
}
|
|
|
|
free(t);
|
|
rc = 0;
|
|
|
|
exit:
|
|
if (b)
|
|
free(b);
|
|
return rc;
|
|
}
|
|
|
|
int poptReadConfigFiles(poptContext con, const char * paths)
|
|
{
|
|
char * buf = (paths ? xstrdup(paths) : NULL);
|
|
const char * p;
|
|
char * pe;
|
|
int rc = 0; /* assume success */
|
|
|
|
for (p = buf; p != NULL && *p != '\0'; p = pe) {
|
|
const char ** av = NULL;
|
|
int ac = 0;
|
|
int i;
|
|
int xx;
|
|
|
|
/* locate start of next path element */
|
|
pe = strchr(p, ':');
|
|
if (pe != NULL && *pe == ':')
|
|
*pe++ = '\0';
|
|
else
|
|
pe = (char *) (p + strlen(p));
|
|
|
|
xx = poptGlob(con, p, &ac, &av);
|
|
|
|
/* work-off each resulting file from the path element */
|
|
for (i = 0; i < ac; i++) {
|
|
const char * fn = av[i];
|
|
if (av[i] == NULL) /* XXX can't happen */
|
|
/*@innercontinue@*/ continue;
|
|
/* XXX should '@' attention be pushed into poptReadConfigFile? */
|
|
if (p[0] == '@' && p[1] != '(') {
|
|
if (fn[0] == '@' && fn[1] != '(')
|
|
fn++;
|
|
xx = poptSaneFile(fn);
|
|
if (!xx && rc == 0)
|
|
rc = POPT_ERROR_BADCONFIG;
|
|
/*@innercontinue@*/ continue;
|
|
}
|
|
xx = poptReadConfigFile(con, fn);
|
|
if (xx && rc == 0)
|
|
rc = xx;
|
|
free((void *)av[i]);
|
|
av[i] = NULL;
|
|
}
|
|
free(av);
|
|
av = NULL;
|
|
}
|
|
|
|
/*@-usedef@*/
|
|
if (buf)
|
|
free(buf);
|
|
/*@=usedef@*/
|
|
|
|
return rc;
|
|
}
|
|
|
|
int poptReadDefaultConfig(poptContext con, /*@unused@*/ UNUSED(int useEnv))
|
|
{
|
|
static const char _popt_sysconfdir[] = POPT_SYSCONFDIR "/popt";
|
|
static const char _popt_etc[] = "/etc/popt";
|
|
char * home;
|
|
struct stat sb;
|
|
int rc = 0; /* assume success */
|
|
|
|
if (con->appName == NULL) goto exit;
|
|
|
|
if (strcmp(_popt_sysconfdir, _popt_etc)) {
|
|
rc = poptReadConfigFile(con, _popt_sysconfdir);
|
|
if (rc) goto exit;
|
|
}
|
|
|
|
rc = poptReadConfigFile(con, _popt_etc);
|
|
if (rc) goto exit;
|
|
|
|
#if defined(HAVE_GLOB_H)
|
|
if (!stat("/etc/popt.d", &sb) && S_ISDIR(sb.st_mode)) {
|
|
const char ** av = NULL;
|
|
int ac = 0;
|
|
int i;
|
|
|
|
if ((rc = poptGlob(con, "/etc/popt.d/*", &ac, &av)) == 0) {
|
|
for (i = 0; rc == 0 && i < ac; i++) {
|
|
const char * fn = av[i];
|
|
if (fn == NULL || strstr(fn, ".rpmnew") || strstr(fn, ".rpmsave"))
|
|
continue;
|
|
if (!stat(fn, &sb)) {
|
|
if (!S_ISREG(sb.st_mode) && !S_ISLNK(sb.st_mode))
|
|
continue;
|
|
}
|
|
rc = poptReadConfigFile(con, fn);
|
|
free((void *)av[i]);
|
|
av[i] = NULL;
|
|
}
|
|
free(av);
|
|
av = NULL;
|
|
}
|
|
}
|
|
if (rc) goto exit;
|
|
#endif
|
|
|
|
if ((home = getenv("HOME"))) {
|
|
char * fn = malloc(strlen(home) + 20);
|
|
if (fn != NULL) {
|
|
(void) stpcpy(stpcpy(fn, home), "/.popt");
|
|
rc = poptReadConfigFile(con, fn);
|
|
free(fn);
|
|
} else
|
|
rc = POPT_ERROR_ERRNO;
|
|
if (rc) goto exit;
|
|
}
|
|
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
poptContext
|
|
poptFini(poptContext con)
|
|
{
|
|
return poptFreeContext(con);
|
|
}
|
|
|
|
poptContext
|
|
poptInit(int argc, const char ** argv,
|
|
const struct poptOption * options, const char * configPaths)
|
|
{
|
|
poptContext con = NULL;
|
|
const char * argv0;
|
|
|
|
if (argv == NULL || argv[0] == NULL || options == NULL)
|
|
return con;
|
|
|
|
if ((argv0 = strrchr(argv[0], '/')) != NULL) argv0++;
|
|
else argv0 = argv[0];
|
|
|
|
con = poptGetContext(argv0, argc, (const char **)argv, options, 0);
|
|
if (con != NULL&& poptReadConfigFiles(con, configPaths))
|
|
con = poptFini(con);
|
|
|
|
return con;
|
|
}
|