#ident "@(#)devlinks.c 1.15 95/01/30 SMI" /* * Copyright (c) 1991 by Sun Microsystems, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "devlinkhdr.h" const char *table_file = TABLE_FILENAME; const char *devdir_pfx; struct devdef ddef; struct miscdev misc[MAX_MISC]; const struct miscdev **extra; const struct miscdev userdef = {0}; /* Dummy entry for extra array */ #define OBPLSFILE "/etc/.obp_devices" FILE *obpfp; boolean_t debugging = B_FALSE; /* -d debugging flag */ /* * addmnode -- add link node to internal structure. * * This adds information about a possible link to an internal structure. * This routine can be called from two places: * get_dev_entries, which finds what links already exist. In this case * the links are marked as 'dangling'. * get_devfs_entries, which finds what links *should* exist. Often the * corresponding link will already exist, and will be marked as * 'valid'. */ void addmnode(const char *devnm, const char *devfsnm, /* Devfs name */ boolean_t in_dev) /* This entry found in /dev */ { int m_no = -1; #ifdef DEBUG printf("Adding %s link from %s to %s\n", (in_dev ? "DEV" : "DEVFS"), devnm, devfsnm); #endif for (m_no = 0; m_no < MAX_MISC; m_no++) { if (misc[m_no].unit == 0 || strcmp(misc[m_no].unit, devnm) == 0) { break; } } if (m_no >= MAX_MISC) { wmessage("devlinks: Too many %s devices - device %s%s ignored\n", ddef.dfs.type, ddef.devdir, devnm); return; } /* * Don`t process more than ONE devfs entry; clones, with * multiple hardware, will have more than one identical entries * bugid 1104276 */ if (misc[m_no].state == LN_MISSING && in_dev == B_FALSE) { return; } if (misc[m_no].unit == NULL) misc[m_no].unit = s_strdup(devnm); if (misc[m_no].devfsnm) { if (strcmp(misc[m_no].devfsnm, devfsnm) != 0) { /* * Assume devfs entry (done second) has precedence */ free(misc[m_no].devfsnm); misc[m_no].devfsnm = s_strdup(devfsnm); misc[m_no].state = LN_INVALID; } else misc[m_no].state = LN_VALID; } else { misc[m_no].devfsnm = s_strdup(devfsnm); misc[m_no].state = (in_dev) ? LN_DANGLING : LN_MISSING; } misc[m_no].extra = -1; /* Mark as not yet in 'extra' structure */ return; } /* * Find existing dev entries matching a particular name-spec */ /* * check to see if a particular name matches a format. * If the format contains a '\N', the output parameter counter is set * to reflect the value of the first \N matched */ static boolean_t fmt_matchdevnm(const char *fmt, const char *name, int * const counter) { const char *cp = name; char sbuf[64]; char *sp; int minval; int num; boolean_t counter_seen = B_FALSE; for (;*fmt != NULL; fmt++) { switch (*fmt) { case '\\': fmt++; switch (*fmt) { case '\0': return(B_FALSE); case '\\': if (*cp++ != '\\') return(B_FALSE); break; case 'N': fmt++; if (!isdigit(*fmt)) return(B_FALSE); minval = *fmt - '0'; sp = sbuf; while (isdigit(*cp)) *sp++ = *cp++; if (sp == sbuf) return(B_FALSE); *sp = '\0'; if (!counter_seen) { num = atol(sbuf); if (num < minval) return(B_FALSE); *counter = num; counter_seen = B_TRUE; } break; default: return(B_FALSE); } fmt++; break; default: if (*cp++ != *fmt++) return(B_FALSE); } } if (*cp == '\0') return(B_TRUE); else return(B_FALSE); } void get_dev_entries() { CACHE_DIR *dp; const CACHE_ENT *entp; int i; /* * Search a directory for special names */ dp = cache_opendir(ddef.devdir); if (dp == NULL) { fmessage(1, "devlinks: Could not open directory '%s': %s\n", ddef.devdir, strerror(errno)); return; } while ((entp = cache_readdir(dp)) != NULL) { /* * Ignore entries that are not symbolic links -- those with their * linkto fields null */ if (entp->linkto == NULL) continue; if (ddef.devspec[0] != '\0') if (!fmt_matchdevnm(ddef.devspec, entp->name, &i)) continue; /* * Add new entry to device node list */ addmnode(entp->name, entp->linkto, B_TRUE); } cache_closedir(dp); return; } static int devfsmatch(const char *string) { register int i; register const char *sp; if (ddef.dfs.name && strcmp(ddef.dfs.name, getdevname(string)) != 0) { return 1; } for (i=0; ilinkto == NULL) continue; if (strncmp(entp->name, fmtbuf, strlen(fmtbuf)) != 0) continue; if (strlen(after_counter) != 0) { if (strcmp(entp->name + strlen(entp->name) - strlen(after_counter), after_counter) != 0) continue; } num = 0; num_length = (strlen(entp->name) - strlen(after_counter) - strlen(fmtbuf)); if (num_length <= 0) continue; rp = entp->name + strlen(fmtbuf); for (i = 0; i < num_length; i++) { if (!isdigit(rp[i])) break; num = num * 10 + (rp[i] - '0'); } if (rp < (entp->name + strlen(entp->name) - strlen(after_counter))) continue; /* Failed the 'isdigit' test */ /* Have valid name; check for match */ if (strcmp(link_val, entp->linkto) != 0) { num_used[ucnt++] = num; continue; } /* Found it! */ strcat(fmtbuf, entp->name + strlen(fmtbuf)); *counter = num; cache_closedir(dp); return(fmtbuf); } cache_closedir(dp); /* * Not found, so use lowest unused greater than counter */ j = count_start; do { k = j; for (i=0; i< ucnt; i++) { if (num_used[i] == k) j++; } } while (k != j); *counter =j; sprintf(fmtbuf+strlen(fmtbuf), "%d%s", j, after_counter); } else if (counter_seen) { sprintf(fmtbuf+strlen(fmtbuf), "%d%s", *counter, after_counter); } return(fmtbuf); } #define N_FSCACHE 20 struct devfscache dfsch[N_FSCACHE]; int devcounter; /* * devfs_entry -- routine called when a matching devfs entry is found * * This routine is called by devfs_find() when a matching devfs entry is found. * It is passwd the name of the devfs entry. */ void devfs_entry(const char *devfsnm) { const char *cp, *tp; char dev_unit[PATH_MAX]; char devfs_fnm[PATH_MAX]; /* * Check entry against devfspfx to check for match */ cp = strrchr(devfsnm, '/'); if (cp == NULL) cp = devfsnm; else cp++; /* Skip the '/' */ if (devfsmatch(cp) != 0) return; sprintf(devfs_fnm, "%s%s", ddef.pathto_devfs, devfsnm); switch (ddef.devpat_type) { case PAT_ABSOLUTE: strcpy(dev_unit, ddef.devspec); break; case PAT_DERIVED: if ((tp = fmt_derived_devnm(ddef.devspec, devfsnm)) == NULL) { wmessage("devlinks: Pattern '%s' cannot be used with device '%s'\n", ddef.devspec, devfsnm); return; } strcpy(dev_unit, tp); break; case PAT_COUNTER: strcpy(dev_unit, fmt_counter_devnm(ddef.devspec, ddef.devdir, devfs_fnm, &devcounter)); devcounter++; break; default: assert(B_FALSE); } addmnode(dev_unit, devfs_fnm, B_FALSE); } /*ARGSUSED*/ void devfs_cache_entry(const char *devfsnm, const char *devtype, const dev_info_t *dip, struct ddi_minor_data *dmip, struct ddi_minor_data *dmap) { struct devfscache *chp, *curchp; int i; if (debugging) { fprintf(stderr, "'%s' entry: %s\n", devtype, devfsnm); /**/ } if (obpfp != NULL) { fprintf(obpfp, "%s %s\n", devtype, devfsnm); } chp = s_malloc(sizeof(*chp)); chp->name = s_strdup(devfsnm); chp->next = NULL; for (i=0; i < N_FSCACHE; i++) { if (dfsch[i].name == NULL) break; if (strcmp(dfsch[i].name, devtype) == 0) { for (curchp = &dfsch[i]; curchp->next != NULL; curchp = curchp->next); curchp->next = chp; curchp = chp; return; } } if (i >= N_FSCACHE) fmessage(4, "devlinks: Too many different device types -- internal error\n"); dfsch[i].name = s_strdup(devtype); dfsch[i].next = chp; } void get_devfs_entries(void) { static int cacheread = B_FALSE; int i; struct devfscache *chp; devcounter = -1; if (!cacheread) { devfs_find(NULL, devfs_cache_entry, 0); cacheread = B_TRUE; } for (i=0; i < N_FSCACHE; i++) { if (dfsch[i].name == NULL) break; if (strcmp(dfsch[i].name, ddef.dfs.type) == 0) { for (chp = dfsch[i].next; chp != NULL; chp = chp->next) devfs_entry(chp->name); return; } } } void remove_links(void) { int i; char devnbuf[PATH_MAX+1]; for (i=0; i %s\n", ddef.devdir, misc[i].unit, misc[i].devfsnm); continue; } if (cache_symlink(misc[i].devfsnm, ddef.devdir, misc[i].unit) != 0) { wmessage("devlinks: Could not create symlink '%s': %s\n", misc[i].unit, strerror(errno)); } } } /* * Extra link stuff, for counted extra links */ static struct miscdev * find_entry(const char *name) { int i; for (i=0; iname, ddef.extra_linkspec, &i) == B_FALSE) continue; /* * Now find if link points to existing dev device. * If it is dangling, ignore it; * if it points to incorrect device, ignore it; * If it points to valid device, set extra[i] pointer. */ if (strncmp(entp->linkto, ddef.extra_linkdir, strlen(ddef.extra_linkdir)) == 0) { /* XXX should check devnm here as well */ if ((mp = find_entry(entp->linkto+strlen(ddef.extra_linkdir))) != NULL) { extra[i] = mp; mp->extra = i; /* self-referential */ } } else extra[i] = &userdef; } cache_closedir(dp); } void remove_extra_links(void) { } static const char * get_pathto(const char *fpath, const char *tpath) { const char *otpath = tpath; static char obuf[PATH_MAX+1]; char *op = obuf; while (*fpath == *tpath && *fpath != '\0') fpath++, tpath++; /* Count directories to go up */ while (*fpath != '\0') { if (*fpath == '/') { strcpy(op, "../"); op += 3; } fpath++; } /* Now tag on directories to go down */ while (tpath != otpath && *(tpath-1) != '/') tpath--; strcpy(op, tpath); return obuf; } void add_extra_links(void) { int i, j; char *name; char linktobuf[PATH_MAX]; j = -1; for (i=0; i %s\n", ddef.extra_linkdir, name, linktobuf); continue; } if (cache_symlink(linktobuf, ddef.extra_linkdir, name) != 0) { wmessage("devlinks: Could not create symlink '%s': %s\n", name, strerror(errno)); } misc[i].extra = j; j++; } } } boolean_t parse_dfs_spec(char *sbuf) { char *vp, *ep; int i; unsigned int subno; int found_type = 0; int found_name = 0; /* * First tidy up old values */ if (ddef.dfs.type) { free (ddef.dfs.type); ddef.dfs.type = NULL; } if (ddef.dfs.name) { free (ddef.dfs.name); ddef.dfs.name = NULL; } for (i=0; i= MAX_DEVFS_COMPMATCH) return(B_FALSE); } else if (strncmp(sbuf, CFS_MINOR, strlen(CFS_MINOR)) == 0) { if (sbuf[strlen(CFS_MINOR)] == '\0') { subno = 0; } else if (isdigit(sbuf[strlen(CFS_MINOR)])) { subno = strtoul(&sbuf[strlen(CFS_MINOR)], NULL, 10); } else return(B_FALSE); i=0; while (i < MAX_DEVFS_COMPMATCH) { if (ddef.dfs.minor[i].pat == NULL) { /* Free slot, so fill it */ ddef.dfs.minor[i].no = subno; ddef.dfs.minor[i].pat = s_strdup(vp); break; } if (ddef.dfs.minor[i].no == subno) return(B_FALSE); i++; } if (i >= MAX_DEVFS_COMPMATCH) return(B_FALSE); } else return(B_FALSE); sbuf = ep; } /* * The type field is required, but we cannot have more than one name. */ if ((found_type != 1) || (found_name > 1)) return (B_FALSE); else return (B_TRUE); } pattype_t find_pattern_type(const char *pattern) { pattype_t rc = PAT_ABSOLUTE; while ((pattern = strchr(pattern, '\\')) != NULL) { pattern++; switch (*pattern) { case 'A': case 'M': if (!isdigit(*(pattern+1))) return (PAT_INVALID); /* FALLTHRU */ case 'D': if (rc == PAT_COUNTER) return (PAT_INVALID); rc = PAT_DERIVED; break; case 'N': if (!isdigit(*(pattern+1))) return (PAT_INVALID); if (rc == PAT_DERIVED || rc == PAT_COUNTER) return(PAT_INVALID); rc = PAT_COUNTER; break; case '\\': break; default: return(PAT_INVALID); } pattern++; } return(rc); } static FILE *table_fp = NULL; boolean_t get_misctbl_entry() { char *cp, *sp, *tmp; int i; char lnbuf[1024]; static unsigned int lncnt = 0; while ((tmp = fgets(lnbuf, sizeof(lnbuf), table_fp)) != NULL) { lncnt++; i = strlen(lnbuf); if (lnbuf[i-1] == '\n') lnbuf[i-1] = '\0'; else if (i == sizeof(lnbuf)-1) { wmessage("devlinks: Line %d too long in configuration file %s \ -- should be less than %d characters\n", lncnt, table_file, sizeof(lnbuf)-1); while ((i = getc(table_fp)) != '\n' && i != EOF); continue; } if (lnbuf[0] == '\0' || lnbuf[0] == '#') continue; /* Ignore comments and blank lines */ /* * Now parse into fields, with CF_SEPCHAR as separator; */ if ((cp = strchr(lnbuf, CF_SEPCHAR)) == NULL) { wmessage("devlinks: Line %d in configuration file incorrect -- ignoring\n", lncnt); continue; } *cp = '\0'; /* Parse devfs spec */ if (parse_dfs_spec(lnbuf) == B_FALSE) { wmessage("devlinks: Line %d in configuration file incorrect -- ignoring\n", lncnt); continue; } sp = cp+1; if ((cp = strchr(sp, CF_SEPCHAR)) != NULL) { *cp = '\0'; strcpy(ddef.devpat, sp); sp = cp+1; if ((cp = strchr(sp, CF_SEPCHAR)) != NULL) { wmessage("devlinks: Line %d in configuration file has too many fields -- ignoring\n", lncnt); continue; } strcpy(ddef.extra_linkpat, sp); } else { strcpy(ddef.devpat, sp); ddef.extra_linkpat[0] = '\0'; } /* Check devpat for correctness -- set flags appropriately */ if ((ddef.devpat_type = find_pattern_type(ddef.devpat)) == PAT_INVALID) { wmessage("devlinks: Line %d in configuration file: devpat field '%s' \ incorrect, ignoring\n", lncnt, ddef.devpat); continue; } if (ddef.extra_linkpat[0] != '\0' && find_pattern_type(ddef.extra_linkpat) != PAT_COUNTER) { wmessage("devlinks: Line %d in configuration file: extra link field '%s' \ incorrect, ignoring line\n", lncnt, ddef.extra_linkpat); continue; } break; } if (tmp == NULL) return (B_FALSE); strcpy(ddef.devdir, devdir_pfx); if ((cp = strrchr(ddef.devpat, '/')) == NULL) { ddef.devspec = ddef.devpat; } else { strncat(ddef.devdir, ddef.devpat, (cp - ddef.devpat + 1)); if (! debugging) create_dirs(ddef.devdir); /* Make it exist */ ddef.devspec = cp+1; } if (ddef.extra_linkpat[0]) { strcpy(ddef.extra_linkdir, devdir_pfx); if ((cp = strrchr(ddef.extra_linkpat, '/')) == NULL) { ddef.extra_linkspec = ddef.extra_linkpat; } else { strncat(ddef.extra_linkdir, ddef.extra_linkpat, (cp - ddef.extra_linkpat + 1)); if (! debugging) create_dirs(ddef.extra_linkdir); /* Make it exist */ ddef.extra_linkspec = cp+1; } } ddef.pathto_devfs[0] = '\0'; cp = ddef.devpat; while ((cp = strchr(cp, '/')) != 0) { strcat(ddef.pathto_devfs, "../"); cp++; /* Skip the '/' */ } strcat(ddef.pathto_devfs, "../devices/"); ddef.devcheck = B_FALSE; /* XXX */ return B_TRUE; } void free_globs() { int i; for (i=0; i