/* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights * Reserved. This file contains Original Code and/or Modifications of * Original Code as defined in and that are subject to the Apple Public * Source License Version 1.0 (the 'License'). You may not use this file * except in compliance with the License. Please obtain a copy of the * License at http://www.apple.com/publicsource and read it before using * this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License." * * @APPLE_LICENSE_HEADER_END@ */ /* * Flat File agent * Written by Marc Majka */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEFAULT_FF_DIR "/etc" #define NOTIFY_PREFIX "com.apple.system.lookupd.FF" #define BUFSIZE 8192 #define CACHE_OFF 0 #define CACHE_ON 1 #define CACHE_DISABLED -1 extern uint32_t notify_monitor_file(int token, const char *name, int flags); typedef struct ff_cache_s { int status; int notify_token; pthread_mutex_t lock; long modtime; dsrecord *crecord; } ff_cache_t; static ff_cache_t filecache[] = { {CACHE_OFF, -1, PTHREAD_MUTEX_INITIALIZER, 0, NULL}, /* LUCategoryUser */ {CACHE_OFF, -1, PTHREAD_MUTEX_INITIALIZER, 0, NULL}, /* LUCategoryGroup */ {CACHE_ON, -1, PTHREAD_MUTEX_INITIALIZER, 0, NULL}, /* LUCategoryHost */ {CACHE_OFF, -1, PTHREAD_MUTEX_INITIALIZER, 0, NULL}, /* LUCategoryNetwork */ {CACHE_OFF, -1, PTHREAD_MUTEX_INITIALIZER, 0, NULL}, /* LUCategoryService */ {CACHE_OFF, -1, PTHREAD_MUTEX_INITIALIZER, 0, NULL}, /* LUCategoryProtocol */ {CACHE_OFF, -1, PTHREAD_MUTEX_INITIALIZER, 0, NULL}, /* LUCategoryRpc */ {CACHE_OFF, -1, PTHREAD_MUTEX_INITIALIZER, 0, NULL}, /* LUCategoryMount */ {CACHE_OFF, -1, PTHREAD_MUTEX_INITIALIZER, 0, NULL}, /* LUCategoryPrinter */ {CACHE_OFF, -1, PTHREAD_MUTEX_INITIALIZER, 0, NULL}, /* LUCategoryBootparam */ {CACHE_OFF, -1, PTHREAD_MUTEX_INITIALIZER, 0, NULL}, /* LUCategoryBootp */ {CACHE_OFF, -1, PTHREAD_MUTEX_INITIALIZER, 0, NULL}, /* LUCategoryAlias */ {CACHE_DISABLED, -1, {0}, 0, NULL}, /* NO FILE FOR LUCategoryNetDomain */ {CACHE_OFF, -1, PTHREAD_MUTEX_INITIALIZER, 0, NULL}, /* LUCategoryEthernet */ {CACHE_OFF, -1, PTHREAD_MUTEX_INITIALIZER, 0, NULL}, /* LUCategoryNetgroup */ {CACHE_DISABLED, -1, {0}, 0, NULL}, /* NO FILE FOR LUCategoryInitgroups */ {CACHE_DISABLED, -1, {0}, 0, NULL} /* NO FILE FOR LUCategoryHostServices */ }; typedef struct { char *dir; int flags; dynainfo *dyna; } agent_private; char *categoryFilename[] = { #ifdef _UNIX_BSD_43_ "passwd", #else "master.passwd", #endif "group", "hosts", "networks", "services", "protocols", "rpc", "fstab", "printcap", "bootparams", "bootptab", "aliases", NULL, "ethers", "netgroup", NULL, NULL }; static void add_validation(dsrecord *r, int cat, char *fname, long ts) { char *str; dsdata *d; dsattribute *a; if (r == NULL) return; d = cstring_to_dsdata("lookup_validation"); dsrecord_remove_key(r, d, SELECT_META_ATTRIBUTE); a = dsattribute_new(d); dsrecord_append_attribute(r, a, SELECT_META_ATTRIBUTE); dsdata_release(d); asprintf(&str, "%s %d %lu", fname, cat, ts); d = cstring_to_dsdata(str); dsattribute_append(a, d); dsdata_release(d); free(str); dsattribute_release(a); } char * getLineFromFile(FILE *fp) { char s[BUFSIZE]; char *out; int len; s[0] = '\0'; fgets(s, BUFSIZE, fp); if (s == NULL || s[0] == '\0') return NULL; if (s[0] == '#') { out = copyString("#"); return out; } len = strlen(s); if (s[len] == '\n') len--; s[len] = '\0'; out = copyString(s); return out; } static dsrecord * parse(char *data, int cat) { if (data == NULL) return NULL; if (data[0] == '#') return NULL; switch (cat) { case LUCategoryUser: return ff_parse_user_A(data); case LUCategoryGroup: return ff_parse_group(data); case LUCategoryHost: return ff_parse_host(data); case LUCategoryNetwork: return ff_parse_network(data); case LUCategoryService: return ff_parse_service(data); case LUCategoryProtocol: return ff_parse_protocol(data); case LUCategoryRpc: return ff_parse_rpc(data); case LUCategoryMount: return ff_parse_mount(data); case LUCategoryPrinter: return ff_parse_printer(data); case LUCategoryBootparam: return ff_parse_bootparam(data); case LUCategoryBootp: return ff_parse_bootp(data); case LUCategoryAlias: return ff_parse_alias(data); case LUCategoryNetDomain: return ff_parse_netgroup(data); case LUCategoryEthernet: return ff_parse_ethernet(data); case LUCategoryNetgroup: return ff_parse_netgroup(data); default: return NULL; } return NULL; } static ff_cache_t * load_cache(int cat, ff_cache_t *cache) { dsrecord *lastrec; char *fname; FILE *fp; char *line; dsrecord *item = NULL; char *fpath; int status; struct stat sb; if (cache == NULL) return NULL; if (cache->status != CACHE_ON) return NULL; dsrecord_release(cache->crecord); cache->crecord = NULL; fname = categoryFilename[cat]; if (fname == NULL) return NULL; asprintf(&fpath, "%s/%s", DEFAULT_FF_DIR, fname); memset(&sb, 0, sizeof(struct stat)); status = stat(fpath, &sb); if (status < 0) { free(fpath); return NULL; } cache->modtime = sb.st_mtime; fp = fopen(fpath, "r"); if (fp == NULL) { free(fpath); return NULL; } /* bootptab entries start after a "%%" line */ if (cat == LUCategoryBootp) { while (NULL != (line = getLineFromFile(fp))) { if (!strncmp(line, "%%", 2)) break; freeString(line); line = NULL; } if (line == NULL) { fclose(fp); free(fpath); return 0; } freeString(line); line = NULL; } lastrec = NULL; while (NULL != (line = getLineFromFile(fp))) { if (line[0] == '#') { freeString(line); line = NULL; continue; } item = parse(line, cat); freeString(line); line = NULL; if (item == NULL) continue; add_validation(item, cat, fpath, cache->modtime); if (cache->crecord == NULL) cache->crecord = item; if (lastrec != NULL) lastrec->next = item; lastrec = item; } free(fpath); fclose(fp); return cache; } static ff_cache_t * prep_cache(int cat, ff_cache_t *cache) { u_int32_t status, check; char *s; ff_cache_t *c; if (cache == NULL) return NULL; if (cache->status != CACHE_ON) return NULL; if (cache->notify_token == -1) { s = NULL; asprintf(&s, "%s.%s", NOTIFY_PREFIX, categoryFilename[cat]); status = notify_register_check(s, &(cache->notify_token)); free(s); if (status != NOTIFY_STATUS_OK) return NULL; s = NULL; asprintf(&s, "%s/%s", DEFAULT_FF_DIR, categoryFilename[cat]); status = notify_monitor_file(cache->notify_token, s, 0); free(s); if (status != NOTIFY_STATUS_OK) return NULL; status = notify_check(cache->notify_token, &check); } pthread_mutex_lock(&(cache->lock)); check = 1; if (cache->modtime != 0) { status = notify_check(cache->notify_token, &check); if ((status == NOTIFY_STATUS_OK) && (check == 0)) { pthread_mutex_unlock(&(cache->lock)); return cache; } } c = load_cache(cat, cache); pthread_mutex_unlock(&(cache->lock)); return c; } static ff_cache_t * cache_for_category(int cat) { if ((cat < 0) || (cat > NCATEGORIES)) return NULL; if (filecache[cat].status != CACHE_ON) return NULL; return prep_cache(cat, &(filecache[cat])); } static int cache_enable_check_name(char *n, int *how) { char *p; if (n == NULL) return -1; p = n; *how = CACHE_ON; if (n[0] == '-') { p++; *how = CACHE_OFF; } if (!strcasecmp(p, "user")) return LUCategoryUser; if (!strcasecmp(p, "users")) return LUCategoryUser; if (!strcasecmp(p, "passwd")) return LUCategoryUser; if (!strcasecmp(p, "master.passwd")) return LUCategoryUser; if (!strcasecmp(p, "group")) return LUCategoryGroup; if (!strcasecmp(p, "groups")) return LUCategoryGroup; if (!strcasecmp(p, "host")) return LUCategoryHost; if (!strcasecmp(p, "hosts")) return LUCategoryHost; if (!strcasecmp(p, "network")) return LUCategoryNetwork; if (!strcasecmp(p, "networks")) return LUCategoryNetwork; if (!strcasecmp(p, "service")) return LUCategoryService; if (!strcasecmp(p, "services")) return LUCategoryService; if (!strcasecmp(p, "protocol")) return LUCategoryProtocol; if (!strcasecmp(p, "protocols")) return LUCategoryProtocol; if (!strcasecmp(p, "rpc")) return LUCategoryRpc; if (!strcasecmp(p, "rpcs")) return LUCategoryRpc; if (!strcasecmp(p, "mount")) return LUCategoryMount; if (!strcasecmp(p, "mounts")) return LUCategoryMount; if (!strcasecmp(p, "fstab")) return LUCategoryMount; if (!strcasecmp(p, "printer")) return LUCategoryPrinter; if (!strcasecmp(p, "printers")) return LUCategoryPrinter; if (!strcasecmp(p, "printcap")) return LUCategoryPrinter; if (!strcasecmp(p, "bootparam")) return LUCategoryBootparam; if (!strcasecmp(p, "bootparams")) return LUCategoryBootparam; if (!strcasecmp(p, "bootp")) return LUCategoryBootp; if (!strcasecmp(p, "bootptab")) return LUCategoryBootp; if (!strcasecmp(p, "alias")) return LUCategoryAlias; if (!strcasecmp(p, "aliases")) return LUCategoryAlias; if (!strcasecmp(p, "ether")) return LUCategoryEthernet; if (!strcasecmp(p, "ethers")) return LUCategoryEthernet; if (!strcasecmp(p, "netgroup")) return LUCategoryNetgroup; if (!strcasecmp(p, "netgroups")) return LUCategoryNetgroup; return -1; } static void FF_configure(agent_private *ap) { int status, i, cat, enable; dsrecord *r; dsattribute *a; dsdata *d; if (ap == NULL) return; if (ap->dyna->dyna_config_agent != NULL) { status = (ap->dyna->dyna_config_agent)(ap->dyna, -1, &r); if (status == 0) { d = cstring_to_dsdata("CacheFile"); a = dsrecord_attribute(r, d, SELECT_ATTRIBUTE); dsdata_release(d); if (a != NULL) { for (i = 0; i < a->count; i++) { if (!strcasecmp(dsdata_to_cstring(a->value[i]), "all")) { for (cat = 0; cat < NCATEGORIES; cat++) { if (filecache[cat].status != CACHE_DISABLED) filecache[cat].status = CACHE_ON; } } else if (!strcasecmp(dsdata_to_cstring(a->value[i]), "none")) { for (cat = 0; cat < NCATEGORIES; cat++) { if (filecache[cat].status != CACHE_DISABLED) filecache[cat].status = CACHE_OFF; } } else { cat = cache_enable_check_name(dsdata_to_cstring(a->value[i]), &enable); if ((cat > 0) && (filecache[cat].status != CACHE_DISABLED)) { filecache[cat].status = enable; } } } dsattribute_release(a); } dsrecord_release(r); } } } u_int32_t FF_new(void **c, char *args, dynainfo *d) { agent_private *ap; if (c == NULL) return 1; ap = (agent_private *)calloc(1, sizeof(agent_private)); *c = ap; if (args == NULL) { ap->dir = copyString(DEFAULT_FF_DIR); ap->flags = 0; } else { ap->dir = copyString(args); ap->flags = 1; } ap->dyna = d; FF_configure(ap); system_log(LOG_DEBUG, "Allocated FF 0x%08x", (int)ap); return 0; } u_int32_t FF_free(void *c) { agent_private *ap; if (c == NULL) return 0; ap = (agent_private *)c; if (ap->dir != NULL) free(ap->dir); ap->dir = NULL; system_log(LOG_DEBUG, "Deallocated FF 0x%08x", (int)ap); free(ap); c = NULL; return 0; } u_int32_t cache_query(ff_cache_t *cache, u_int32_t cat, int single_item, dsrecord *pattern, dsrecord **list) { dsrecord *item, *host, *lastrec; int match; dsdata *k, *k4, *k6; dsattribute *a; lastrec = NULL; for (item = cache->crecord; item != NULL; item = item->next) { match = dsrecord_match_select(item, pattern, SELECT_ATTRIBUTE); if (match == 1) { if (*list == NULL) { *list = dsrecord_copy(item); lastrec = *list; } else { lastrec->next = dsrecord_copy(item); lastrec = lastrec->next; } lastrec->next = NULL; if (cat == LUCategoryHost) { } else if (single_item == 1) { break; } } } if ((cat == LUCategoryHost) && (single_item == 1)) { if ((*list) == NULL) return 0; if ((*list)->next == NULL) return 0; k = cstring_to_dsdata("name"); k4 = cstring_to_dsdata("ip_address"); k6 = cstring_to_dsdata("ipv6_address"); host = *list; for (item = host->next; item != NULL; item = item->next) { a = dsrecord_attribute(item, k, SELECT_ATTRIBUTE); dsrecord_merge_attribute(host, a, SELECT_ATTRIBUTE); dsattribute_release(a); a = dsrecord_attribute(item, k4, SELECT_ATTRIBUTE); dsrecord_merge_attribute(host, a, SELECT_ATTRIBUTE); dsattribute_release(a); a = dsrecord_attribute(item, k6, SELECT_ATTRIBUTE); dsrecord_merge_attribute(host, a, SELECT_ATTRIBUTE); dsattribute_release(a); } dsdata_release(k); dsdata_release(k4); dsdata_release(k6); dsrecord_release(host->next); host->next = NULL; } return 0; } u_int32_t FF_query(void *c, dsrecord *pattern, dsrecord **list) { agent_private *ap; u_int32_t cat; dsattribute *a; dsdata *k, *k4, *k6; dsrecord *lastrec; char *fname; FILE *fp; char *line; dsrecord *item = NULL; dsrecord *host = NULL; char *fpath; long ts; int match, single_item, stamp; ff_cache_t *cache; struct stat sb; if (c == NULL) return 1; if (pattern == NULL) return 1; if (list == NULL) return 1; *list = NULL; lastrec = NULL; single_item = 0; stamp = 0; ap = (agent_private *)c; k = cstring_to_dsdata(CATEGORY_KEY); a = dsrecord_attribute(pattern, k, SELECT_META_ATTRIBUTE); dsdata_release(k); if (a == NULL) return 1; if (a->count == 0) return 1; cat = atoi(dsdata_to_cstring(a->value[0])); dsattribute_release(a); fname = categoryFilename[cat]; if (fname == NULL) return 1; k = cstring_to_dsdata(STAMP_KEY); a = dsrecord_attribute(pattern, k, SELECT_META_ATTRIBUTE); dsdata_release(k); if (a != NULL) { dsrecord_remove_attribute(pattern, a, SELECT_META_ATTRIBUTE); stamp = 1; } dsattribute_release(a); k = cstring_to_dsdata(SINGLE_KEY); a = dsrecord_attribute(pattern, k, SELECT_META_ATTRIBUTE); dsdata_release(k); if (a != NULL) { dsattribute_release(a); single_item = 1; } asprintf(&fpath, "%s/%s", ap->dir, fname); ts = 0; cache = NULL; if (ap->flags == 0) cache = cache_for_category(cat); if (cache != NULL) { ts = cache->modtime; } else { memset(&sb, 0, sizeof(struct stat)); if (stat(fpath, &sb) < 0) ts = 0; else ts = sb.st_mtime; } if (stamp == 1) { item = dsrecord_new(); add_validation(item, cat, fpath, ts); *list = item; free(fpath); return 0; } if (cache != NULL) { free(fpath); return cache_query(cache, cat, single_item, pattern, list); } fp = fopen(fpath, "r"); if (fp == NULL) { free(fpath); return 1; } /* bootptab entries start after a "%%" line */ if (cat == LUCategoryBootp) { while (NULL != (line = getLineFromFile(fp))) { if (!strncmp(line, "%%", 2)) break; freeString(line); line = NULL; } if (line == NULL) { fclose(fp); free(fpath); return 0; } freeString(line); line = NULL; } while (NULL != (line = getLineFromFile(fp))) { if (line[0] == '#') { freeString(line); line = NULL; continue; } item = parse(line, cat); freeString(line); line = NULL; if (item == NULL) continue; match = dsrecord_match_select(item, pattern, SELECT_ATTRIBUTE); if (match == 1) { add_validation(item, cat, fpath, ts); if (*list == NULL) *list = dsrecord_retain(item); else lastrec->next = dsrecord_retain(item); lastrec = item; if (cat == LUCategoryHost) { } else if (single_item == 1) { dsrecord_release(item); break; } } dsrecord_release(item); } free(fpath); fclose(fp); if ((cat == LUCategoryHost) && (single_item == 1)) { if ((*list) == NULL) return 0; if ((*list)->next == NULL) return 0; k = cstring_to_dsdata("name"); k4 = cstring_to_dsdata("ip_address"); k6 = cstring_to_dsdata("ipv6_address"); host = *list; for (item = host->next; item != NULL; item = item->next) { a = dsrecord_attribute(item, k, SELECT_ATTRIBUTE); dsrecord_merge_attribute(host, a, SELECT_ATTRIBUTE); dsattribute_release(a); a = dsrecord_attribute(item, k4, SELECT_ATTRIBUTE); dsrecord_merge_attribute(host, a, SELECT_ATTRIBUTE); dsattribute_release(a); a = dsrecord_attribute(item, k6, SELECT_ATTRIBUTE); dsrecord_merge_attribute(host, a, SELECT_ATTRIBUTE); dsattribute_release(a); } dsdata_release(k); dsdata_release(k4); dsdata_release(k6); dsrecord_release(host->next); host->next = NULL; } return 0; } u_int32_t FF_validate(void *c, char *v) { agent_private *ap; int n, cat; u_int32_t ts, status, check; struct stat st; char fpath[MAXPATHLEN + 1]; if (c == NULL) return 0; if (v == NULL) return 0; ap = (agent_private *)c; n = sscanf(v, "%s %d %u", fpath, &cat, &ts); if (n != 3) return 0; if ((cat < 0) || (cat > NCATEGORIES)) return 0; if ((filecache[cat].status == CACHE_ON) && (filecache[cat].notify_token != -1)) { check = 1; status = notify_check(filecache[cat].notify_token, &check); if ((status == NOTIFY_STATUS_OK) && (check == 0)) return 1; filecache[cat].modtime = 0; } if (stat(fpath, &st) < 0) return 0; if (ts == st.st_mtime) return 1; return 0; }