/* * Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 * NOVELL (All rights reserved) * Copyright (c) 2010 - 2012 * Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, contact Canonical, Ltd. */ /* Handle subdomain includes, as a straight forward preprocessing phase. While we are at it we will strip comments. Why? because it made it easier. We support 2 types of includes #include which searches for the first occurance of name in the subdomain directory path. #include "name" which will search for a relative or absolute pathed file -p : preprocess only. Dump output to stdout -I path : add a path to be search by #include < > -b path : set the base path to something other than /etc/subdomain.d */ #include #include #include #include #include #include #include #include #include #include #include #include "lib.h" #include "parser.h" #include "parser_include.h" /* An array of search directories, I sure hope 100's enough */ #define MAX_PATH 100 /* maximum depth of nesting */ #define MAX_NEST_LEVEL 100 /* Location of the subdomain.conf file */ #ifdef SUBDOMAIN_CONFDIR #define SUBDOMAIN_CONF SUBDOMAIN_CONFDIR "/subdomain.conf" #else /* !defined SUBDOMAIN_CONFDIR */ #define SUBDOMAIN_CONF "/etc/subdomain.conf" #endif /* SUBDOMAIN_CONFDIR */ static char *path[MAX_PATH] = { NULL }; static int npath = 0; static int fgetline(FILE * f, char *buffer, size_t len); static int stripcomment(char *s); static char *stripblanks(char *s); /* default base directory is /etc/subdomain.d, it can be overriden with the -b option. */ const char *basedir; static const char *default_basedir = "/etc/apparmor.d"; static const char *old_basedir = "/etc/subdomain.d"; /* set up basedir so that it can be overridden/used later. */ void init_base_dir(void) { int rc; struct stat sbuf; /* basedir should always start out NULL; if not, something's * wrong.*/ assert(basedir == NULL); rc = stat(default_basedir, &sbuf); if (rc == 0 && S_ISDIR(sbuf.st_mode)) { basedir = default_basedir; return; } rc = stat(old_basedir, &sbuf); if (rc == 0 && S_ISDIR(sbuf.st_mode)) { basedir = old_basedir; return; } } /* Set the base dir. Used to change default path for relative includes */ void set_base_dir(char *dir) { char *t; int i, rc; struct stat sbuf; t = strndup(dir, PATH_MAX); if (!t) { PERROR(_("Error: Out of memory.\n")); return; } /*strip trailing /'s */ for (i = strlen(t) - 1; i >= 0 && t[i] == '/'; i--) t[i] = 0; rc = stat(t, &sbuf); if (rc == -1 || !S_ISDIR(sbuf.st_mode)) { PERROR(_("Error: basedir %s is not a directory, skipping.\n"), t); free(t); return; } basedir = t; return; } /* Add a directory to the search path. */ int add_search_dir(const char *dir) { char *t; size_t len; if (npath >= MAX_PATH) { PERROR(_("Error: Could not add directory %s to search path.\n"), dir); return 0; } if (!dir) return 1; len = strlen(dir); if (len == 0) return 1; t = strdup(dir); if (t == NULL) { PERROR(_("Error: Could not allocate memory.\n")); return 0; } /*strip trailing /'s */ while (len > 0 && t[--len] == '/') t[len] = '\0'; path[npath] = t; npath++; return 1; } /* Parse Subdomain.conf and put the default dirs in place. subdomain.conf is a shell sourcable file we only parse entries starting with SUBDOMAIN_PATH= if there are multiple entries with SUBDOMAIN_PATH= each will get added. SUBDOMAIN_PATH=/etc/subdomain.d:/etc/subdomain.d/include is the same as SUBDOMAIN_PATH=/etc/subdomain.d SUBDOMAIN_PATH=/etc/subdomain.d/include */ void parse_default_paths(void) { autofclose FILE *f; char buf[1024]; char *t, *s; int saved_npath = npath; f = fopen(SUBDOMAIN_CONF, "r"); if (f == NULL) goto out; memset(buf, 0, sizeof(buf)); while (fgetline(f, buf, 1024)) { if (stripcomment(buf) && (t = strstr(buf, "SUBDOMAIN_PATH="))) { t += 15; /* handle : separating path elements */ do { s = strchr(t, ':'); if (s) *s = 0; if (!add_search_dir(stripblanks(t))) break; if (s) t = s + 1; } while (s != NULL); } } /* if subdomain.conf doesn't set a base search dir set it to this */ out: if (npath - saved_npath == 0) { add_search_dir(basedir); } } FILE *search_path(char *filename, char **fullpath) { FILE *newf = NULL; char *buf = NULL; int i; for (i = 0; i < npath; i++) { if (asprintf(&buf, "%s/%s", path[i], filename) < 0) { perror("asprintf"); exit(1); } newf = fopen(buf, "r"); if (newf && fullpath) *fullpath = buf; else free(buf); buf = NULL; if (newf) break; } return newf; } /* get a line from the file. If it is to long truncate it. */ static int fgetline(FILE * f, char *buffer, size_t len) { char *b = buffer; int c; while (((c = fgetc(f)) != EOF) && (c != '\n') && (strlen(buffer) < len - 1)) { *b = c; b++; } *b = '\0'; if (c != EOF) return 1; return 0; } /* If there is a comment null terminate the string, return strlen of the stripped string*/ static int stripcomment(char *s) { char *t = s; while (*s != '#' && *s != 0) s++; *s = 0; return strlen(t); } static char *stripblanks(char *s) { char *c; while (isspace(*s)) s++; c = s; while (!isspace(*s) && *s != 0) s++; *s = 0; return c; } struct include_stack_t { char *filename; int lineno; struct include_stack_t *next; }; struct include_stack_t *include_stack_head = NULL; static void start_include_position(const char *filename) { if (current_filename) free(current_filename); current_filename = strdup(filename ? filename : "stdin"); current_lineno = 1; } void push_include_stack(char *filename) { struct include_stack_t *include = NULL; include = (struct include_stack_t *) malloc(sizeof(*include)); if (!include) { perror("malloc of included file stack tracker"); /* failures in this area are non-fatal */ return; } include->filename = strdup(current_filename); include->lineno = current_lineno; include->next = include_stack_head; include_stack_head = include; start_include_position(filename); } void pop_include_stack(void) { struct include_stack_t *include = NULL; if (!include_stack_head) return; include = include_stack_head; include_stack_head = include->next; if (current_filename) free(current_filename); current_filename = include->filename; current_lineno = include->lineno; free(include); } void reset_include_stack(const char *filename) { while (include_stack_head) pop_include_stack(); start_include_position(filename); }