在./modutils-2.4.0/depmod/目录下有一个配置文件的样式,Example.module.conf。
1 # This is an example of additional definitions you can put in /etc/modules.conf
2 # Note that modprobe has some default aliases built in ("modprobe -c").
3 # The built-in aliases will be overridden by any definitions in this file.
4
5 keep # keep the default set of paths and _add_ the following path(s)
6 path[net]=/lib/modules/`uname -r`/some_special_directory
7
8 alias scsi_hostadapter aha1542
9 alias eth0 3c509
10 alias eth1 de620
11 options de620 irq=7 bnc=1
12 # override:
13 alias char-major-14 sound
14
15 # Conditional decoding via: if, else, elseif, endif
16 #
17 # Avoid having "path" definitions in conditional parts,
18 # unless you are _sure_ that the modules.dep file generated
19 # by depmod is always correct whenever modprobe executes.
20 #
21 # version dependence:
22 if `kernelversion` > 2.0
23 alias char-major-14 sb
24 endif
25
26 # Include another config file: include FILE
27 # in this case some additional aliases
28 if -f /etc/devfs.aliases
29 include /etc/devfs.aliases
30 endif
31
32 # Additional dependencies, "pull in"
33 above sb adlib_card
34
35 # Set parameters (also in the environment): define PARAM VALUE
下面以这个文档为例子,分析配置文件的解析过程。
895 /*
896 * endif
897 */
898 if (!assgn && strcmp(parm, "endif") == 0) {
899 if (level > 0)
900 --level;
901 else {
902 error("unmatched endif in line %d", lineno);
903 return -1;
904 }
905 continue;
906 }
907 /*
908 * else
909 */
910 if (!assgn && strcmp(parm, "else") == 0) {
911 if (level <= 0) {
912 error("else without if in line %d", lineno);
913 return -1;
914 }
915 state[level] = !state[level];
916 continue;
917 }
918
919 /*
920 * elseif
921 */
922 if (!assgn && strcmp(parm, "elseif") == 0) {
923 if (level <= 0) {
924 error("elseif without if in line %d", lineno);
925 return -1;
926 }
927 if (state[level] != 0) {
928 /*
929 * We have already found a TRUE
930 * if statement in this "chain".
931 * That's what "2" means.
932 */
933 state[level] = 2;
934 continue;
935 }
936 /* else: No TRUE if has been found, cheat */
937 /*
938 * The "if" handling increments level,
939 * but this is the _same_ level as before.
940 * So, compensate for it.
941 */
942 --level;
943 parm = "if";
944 /* Fallthru to "if" */
945 }
946
947 /*
948 * if
949 */
950 if (strcmp(parm, "if") == 0) {
951 char *cmp;
952 int not = 0;
953 int numeric = 0;
954
955 if (level >= MAX_LEVEL) {
956 error("Too many nested if's in line %d/n", lineno);
957 return -1;
958 }
959 state[++level] = 0; /* default false */
960
961 if (*arg == '!') {
962 not = 1;
963 arg = next_word(arg);
964 }
965
966 if (strncmp(arg, "-k", 2) == 0) {
967 state[level] = flag_autoclean;
968 continue;
969 }
970
971 if (strncmp(arg, "-f", 2) == 0) {
972 char *file = next_word(arg);
973 meta_expand(file, &g, NULL, version, ME_ALL);
974 if (access(g.pathc ? g.pathv[0] : file, R_OK) == 0)
975 state[level] = !not;
976 else
977 state[level] = not;
978 continue;
979 }
980
981 if (strncmp(arg, "-n", 2) == 0) {
982 numeric = 1;
983 arg = next_word(arg);
984 }
985
986
987 cmp = next_word(arg);
988 if (*cmp) {
989 GLOB_LIST g2;
990 long n1 = 0;
991 long n2 = 0;
992 char *w1 = "";
993 char *w2 = "";
994
995 arg2 = next_word(cmp);
996
997 meta_expand(arg, &g, NULL, version, ME_ALL);
998 if (g.pathc && g.pathv[0])
999 w1 = g.pathv[0];
1000
1001 meta_expand(arg2, &g2, NULL, version, ME_ALL);
1002 if (g2.pathc && g2.pathv[0])
1003 w2 = g2.pathv[0];
1004
1005 if (numeric) {
1006 n1 = strtol(w1, NULL, 0);
1007 n2 = strtol(w2, NULL, 0);
1008 }
1009
1010 if (strcmp(cmp, "==") == 0 ||
1011 strcmp(cmp, "=") == 0) {
1012 if (numeric)
1013 state[level] = (n1 == n2);
1014 else
1015 state[level] = strcmp(w1, w2) == 0;
1016 } else if (strcmp(cmp, "!=") == 0) {
1017 if (numeric)
1018 state[level] = (n1 != n2);
1019 else
1020 state[level] = strcmp(w1, w2) != 0;
1021 } else if (strcmp(cmp, ">=") == 0) {
1022 if (numeric)
1023 state[level] = (n1 >= n2);
1024 else
1025 state[level] = strcmp(w1, w2) >= 0;
1026 } else if (strcmp(cmp, "<=") == 0) {
1027 if (numeric)
1028 state[level] = (n1 <= n2);
1029 else
1030 state[level] = strcmp(w1, w2) <= 0;
1031 } else if (strcmp(cmp, ">") == 0) {
1032 if (numeric)
1033 state[level] = (n1 > n2);
1034 else
1035 state[level] = strcmp(w1, w2) > 0;
1036 } else if (strcmp(cmp, "<") == 0) {
1037 if (numeric)
1038 state[level] = (n1 < n2);
1039 else
1040 state[level] = strcmp(w1, w2) < 0;
1041 }
1042 } else { /* Check defined value, if any */
1043 /* undef or defined as
1044 * "" or "0" or "false" => false
1045 * defined => true
1046 */
1047 if (!meta_expand(arg, &g, NULL, version, ME_ALL) &&
1048 g.pathc > 0 &&
1049 strcmp(g.pathv[0], "0") != 0 &&
1050 strcmp(g.pathv[0], "false") != 0 &&
1051 strlen(g.pathv[0]) != 0)
1052 state[level] = 1; /* true */
1053 }
1054 if (not)
1055 state[level] = !state[level];
1056
1057 continue;
1058 }
1059
1060 /*
1061 * Should we bother?
1062 */
1063 if (state[level] != 1)
1064 continue;
1065
1066 /*
1067 * define
1068 */
1069 if (!assgn && strcmp(parm, "define") == 0) {
1070 char env[PATH_MAX];
1071
1072 arg2 = next_word(arg);
1073 meta_expand(arg2, &g, NULL, version, ME_ALL);
1074 snprintf(env, sizeof(env), "%s=%s", arg, (g.pathc ? g.pathv[0] : ""));
1075 putenv(env);
1076 one_err = 0;
1077 }
1078
1079 /*
1080 * include
1081 */
1082 if (!assgn && strcmp(parm, "include") == 0) {
1083 meta_expand(arg, &g, NULL, version, ME_ALL);
1084
1085 if (!do_read(all, version, base_dir, g.pathc ? g.pathv[0] : arg, depth+1))
1086 one_err = 0;
1087 else
1088 error("include %s failed/n", arg);
1089 }
1090
1091 /*
1092 * above
1093 */
1094 else if (all && !assgn && strcmp(parm, "above") == 0) {
1095 decode_list(&n_abovelist, &abovelist, arg, adding, version, 0);
1096 one_err = 0;
1097 }
1098
1099 /*
1100 * below
1101 */
1102 else if (all && !assgn && strcmp(parm, "below") == 0) {
1103 decode_list(&n_belowlist, &belowlist, arg, adding, version, 0);
1104 one_err = 0;
1105 }
1106
1107 /*
1108 * prune
1109 */
1110 else if (all && !assgn && strcmp(parm, "prune") == 0) {
1111 decode_list(&n_prunelist, &prunelist, arg, adding, version, 0);
1112 one_err = 0;
1113 }
1114
1115 /*
1116 * probe
1117 */
1118 else if (all && !assgn && strcmp(parm, "probe") == 0) {
1119 decode_list(&n_probe_list, &probe_list, arg, adding, version, 0);
1120 one_err = 0;
1121 }
1122
1123 /*
1124 * probeall
1125 */
1126 else if (all && !assgn && strcmp(parm, "probeall") == 0) {
1127 decode_list(&n_probeall_list, &probeall_list, arg, adding, version, 0);
1128 one_err = 0;
1129 }
1130
1131 /*
1132 * options
1133 */
1134 else if (all && !assgn && strcmp(parm, "options") == 0) {
1135 decode_list(&n_opt_list, &opt_list, arg, adding, version, 1);
1136 one_err = 0;
1137 }
1138
1139 /*
1140 * alias
1141 */
1142 else if (all && !assgn && strcmp(parm, "alias") == 0) {
1143 /*
1144 * Replace any previous (default) definitions
1145 * for the same module
1146 */
1147 decode_list(&n_aliases, &aliases, arg, 0, version, 0);
1148 one_err = 0;
1149 }
1150
1151 /*
1152 * Specification: /etc/modules.conf
1153 * The format of the commands in /etc/modules.conf are:
1154 *
1155 * pre-install module command
1156 * install module command
1157 * post-install module command
1158 * pre-remove module command
1159 * remove module command
1160 * post-remove module command
1161 *
1162 * The different words are separated by tabs or spaces.
1163 */
1164 /*
1165 * pre-install
1166 */
1167 else if (all && !assgn && (strcmp(parm, "pre-install") == 0)) {
1168 decode_exec(arg, EXEC_PRE_INSTALL);
1169 one_err = 0;
1170 }
1171
1172 /*
1173 * install
1174 */
1175 else if (all && !assgn && (strcmp(parm, "install") == 0)) {
1176 decode_exec(arg, EXEC_INSTALL);
1177 one_err = 0;
1178 }
1179
1180 /*
1181 * post-install
1182 */
1183 else if (all && !assgn && (strcmp(parm, "post-install") == 0)) {
1184 decode_exec(arg, EXEC_POST_INSTALL);
1185 one_err = 0;
1186 }
1187
1188 /*
1189 * pre-remove
1190 */
1191 else if (all && !assgn && (strcmp(parm, "pre-remove") == 0)) {
1192 decode_exec(arg, EXEC_PRE_REMOVE);
1193 one_err = 0;
1194 }
1195
1196 /*
1197 * remove
1198 */
1199 else if (all && !assgn && (strcmp(parm, "remove") == 0)) {
1200 decode_exec(arg, EXEC_REMOVE);
1201 one_err = 0;
1202 }
1203
1204 /*
1205 * post-remove
1206 */
1207 else if (all && !assgn && (strcmp(parm, "post-remove") == 0)) {
1208 decode_exec(arg, EXEC_POST_REMOVE);
1209 one_err = 0;
1210 }
1211
1212 /*
1213 * insmod_opt=
1214 */
1215 else if (assgn && (strcmp(parm, "insmod_opt") == 0)) {
1216 insmod_opt = xstrdup(arg);
1217 one_err = 0;
1218 }
1219
1220 /*
1221 * keep
1222 */
1223 else if (!assgn && (strcmp(parm, "keep") == 0)) {
1224 drop_default_paths = 0;
1225 one_err = 0;
1226 }
1227
1228 /*
1229 * path...=
1230 */
1231 else if (assgn && strncmp(parm, "path", 4) == 0) {
1232 /*
1233 * Specification: config file / path parameter
1234 * The path parameter specifies a directory to
1235 * search for modules.
1236 * This parameter may be repeated multiple times.
1237 *
1238 * Note that the actual path may be defined using
1239 * wildcards and other shell meta-chars, such as "*?`".
1240 * For example:
1241 * path[misc]=/lib/modules/1.1.5?/misc
1242 *
1243 * Optionally the path keyword carries a tag.
1244 * This tells us a little more about the purpose of
1245 * this directory and allows some automated operations.
1246 * A path is marked with a tag by adding the tag,
1247 * enclosed in square brackets, to the path keyword:
1248 * #
1249 * path[boot]=/lib/modules/boot
1250 * #
1251 * This case identifies the path a of directory
1252 * holding modules loadable a boot time.
1253 */
1254
1255 if (drop_default_paths) {
1256 int n;
1257
1258 /*
1259 * Specification: config file / path / default
1260 *
1261 * Whenever there is a path[] specification
1262 * in the config file, all the default
1263 * path are reset.
1264 *
1265 * If one instead wants to _add_ to the default
1266 * set of paths, one has to have the option
1267 * keep
1268 * before the first path[]-specification line
1269 * in the configuration file.
1270 */
1271 drop_default_paths = 0;
1272 for (n = 0; n < nmodpath; n++) {
1273 free(modpath[n].path);
1274 free(modpath[n].type);
1275 }
1276 nmodpath = 0;
1277 }
1278
1279 /*
1280 * Get (the optional) tag
1281 * If the tag is missing, the word "misc"
1282 * is assumed.
1283 */
1284 type = "misc";
1285
1286 if (parm[4] == '['] {
1287 char *pt_type = parm + 5;
1288 while (*pt_type != '/0' && *pt_type != ')')
1289 pt_type++;
1290
1291 if (*pt_type == ')' && pt_type[1] == '/0') {
1292 *pt_type = '/0';
1293 type = parm + 5;
1294 } /* else CHECKME */
1295 }
1296
1297 /*
1298 * Handle the actual path description
1299 */
1300 if (meta_expand(arg, &g, base_dir, version, ME_ALL))
1301 return -1;
1302 for (glb = g.pathv; glb && *glb; ++glb) {
1303 modpath[nmodpath].type = xstrdup(type);
1304 modpath[nmodpath].path = *glb;
1305 if (++nmodpath >= maxpath) {
1306 maxpath += 100;
1307 modpath = (struct PATH_TYPE *)xrealloc(modpath,
1308 maxpath * sizeof(struct PATH_TYPE));
1309 }
1310 }
1311 one_err = 0;
1312 }
1313
1314 /*
1315 * persistdir
1316 */
1317 else if (assgn && strcmp(parm, "persistdir") == 0) {
1318 meta_expand(arg, &g, NULL, version, ME_ALL);
1319 persistdir = xstrdup(g.pathc ? g.pathv[0] : arg);
1320 one_err = 0;
1321 }
1322
1323 /* Names for generated files in config file */
1324 for (i = 0; one_err && i < gen_file_count; ++i)
1325 one_err = gen_file_conf(gen_file+i, assgn, parm, arg);
1326
1327 /*
1328 * any errors so far?
1329 */
1330 if (all == 0)
1331 one_err = 0;
1332 else if (one_err) {
1333 error("Invalid line %d in %s/n/t%s",
1334 lineno, conf_file, buf);
1335 ret = -1;
1336 }
1337}
首先看Example.module.conf的第5行。经过866~893行的处理,parm=”keep”,arg=0。然后进入1223行的else if块,将drop_default_paths,one_error置为0。至此,对第5行的处理结束。
读取第6行,经过866~893行后,parm=”path[net]”,arg=”/lib/modules/`uname -r`/ some_special_directory”,assign=1。注意,在path[net]和=之间不能有空格,否则通不过884行的if语句。然后进入1231行的else if块。首先注意1232~1253行的注释,注释中提到path可以使用[…]这样的标识,以方便自动处理程序,在这里tag是net。另外,在路径中可以使用Shell里的元符号,真是功能强大啊。在1259~1269行的注释中说明,如果在所有的path命令前声明keep,默认的path配置都将保留,否则将覆盖,这里声明了keep。因此跳过了1255~1277行。
1286~1296行提取path的标识。
在1301行,meta_expand函数对arg指向的路径进行元符号转换处理,函数的第2个参数g,是一个GLOB_LIST结构,该结构定义于./modutils-2.4.0/include/util.h。
65 /*
66 * Generic globlist <[email protected]>
67 */
68 typedef struct {
69 int pathc; /* Count of paths matched so far */
70 char **pathv; /* List of matched pathnames. */
71 } GLOB_LIST;
函数meta_expand的定义在./modutils-2.4.0/util/meta_expand.c中。
152 /*
153 * Expand the string (including meta-character) to a list of matches
154 *
155 * Return 0 if OK else -1
156 */
157 int meta_expand(char *pt, GLOB_LIST *g, char *base_dir, char *version, int type)
158 {
159 FILE *fin;
160 int len = 0;
161 char *line = NULL;
162 char *p, *p1;
163 char tmpline[PATH_MAX + 1];
164 char wrk[sizeof(tmpline)];
165 char tmpcmd[2*sizeof(tmpline)+20]; /* room for /bin/echo "text" */
166
167 g->pathc = 0;
168 g->pathv = NULL;
169
170 /*
171 * Take care of version dependent expansions
172 * Needed for forced version handling
173 */
174 if ((p = strchr(pt, '`')) != NULL && (type & ME_BUILTIN_COMMAND)) {
175 do {
176 char *s;
177
178 for (s = p + 1; isspace(*s); ++s)
179 ;
180
181 if (strncmp(s, "uname -r", 8) == 0) {
182 while (*s && (*s != '`'))
183 ++s;
184 if (*s == '`') {
185 *p = '/0';
186 snprintf(wrk, sizeof(wrk), "%s%s%s",
187 pt,
188 version,
189 s + 1);
190 *p = '`';
191 }
192 strcpy(tmpline, wrk); /* safe, same size */
193 pt = tmpline;
194 } else if (strncmp(s, "kernelversion", 13) == 0) {
195 while (*s && (*s != '`'))
196 ++s;
197 if (*s == '`') {
198 int n;
199 char *k;
200
201 *p = '/0';
202 for (n = 0, k = version; *k; ++k) {
203 if (*k == '.' && ++n == 2)
204 break;
205 }
206 snprintf(wrk, sizeof(wrk), "%s%.*s%s",
207 pt,
208 /* typecast for Alpha */
209 (int)(k - version),
210 version,
211 s + 1);
212 *p = '`';
213 strcpy(tmpline, wrk); /* safe, same size */
214 pt = tmpline;
215 }
216 } else
217 break;
218 } while ((p = strchr(pt, '`')) != NULL);
219 }
220
221 /*
222 * Any remaining meta-chars?
223 */
224 if (strpbrk(pt, SHELL_META) == NULL) {
225 /*
226 * No meta-chars.
227 * Split into words, delimited by whitespace.
228 */
229 snprintf(wrk, sizeof(wrk), "%s%s", (base_dir ? base_dir : ""), pt);
230 strcpy(tmpline, wrk); /* safe, same size */
231 if ((p = strtok(tmpline, " /t/n")) != NULL) {
232 while (p) {
233 g->pathv = (char **)xrealloc(g->pathv,
234 (g->pathc + 2) * sizeof(char *));
235 g->pathv[g->pathc++] = xstrdup(p);
236 p = strtok(NULL, " /t/n");
237 }
238 }
239 if (g->pathc)
240 g->pathv[g->pathc] = NULL;
241 return 0;
242 }
243 /* else */
244 /*
245 * Handle remaining meta-chars
246 */
247
248 /*
249 * Just plain quotes?
250 */
251 if (strpbrk(pt, "&();|<>$`!{}[]~=+:?*") == NULL &&
252 (p = strpbrk(pt, "/"'//"))) {
253 split_line(g, pt, 1);
254 return 0;
255 }
256
257 if (strpbrk(pt, "&();|<>$`/"'//!{}~+:[]~?*") == NULL) {
258 /* Only "=" remaining, should be module options */
259 split_line(g, pt, 0);
260 return 0;
261 }
262
263 /*
264 * If there are meta-characters and
265 * if they are only shell glob meta-characters: do globbing
266 */
267 #if HAVE_WORDEXP
268 if (strpbrk(pt, "&();|<>`/"'//!{}~=+:") == NULL &&
269 strpbrk(pt, "$[]~?*"))
270 #else
271 if (strpbrk(pt, "&();|<>$`/"'//!{}~=+:") == NULL &&
272 strpbrk(pt, "[]~?*"))
273 #endif
274 if ((type & ME_GLOB) && glob_it(pt, g) == 0)
275 return 0;
276
277 if (strpbrk(pt, "&();|<>$`/"'//!{}~+:[]~?*") == NULL) {
278 /* Only "=" remaining, should be module options */
279 split_line(g, pt, 0);
280 return 0;
281 }
282
283 /*
284 * Last resort: Use "echo".
285 * DANGER: Applying shell expansion to user supplied input is a
286 * major security risk. Modutils code should only do meta
287 * expansion via shell commands for trusted data. Basically
288 * this means only for data in the config file. Even that
289 * assumes that the user cannot run modprobe as root with
290 * their own config file. Programs (including the kernel)
291 * that invoke modprobe as root with user supplied input must
292 * pass exactly one user supplied parameter and must set
293 * safe mode.
294 */
295 if (!(type & ME_SHELL_COMMAND))
296 return 0;
297 snprintf(wrk, sizeof(wrk), "%s%s", (base_dir ? base_dir : ""), pt);
298 strcpy(tmpline, wrk); /* safe, same size */
299 snprintf(tmpcmd, sizeof(tmpcmd), "/bin/echo /"");
300 for (p = tmpline, p1 = tmpcmd + strlen(tmpcmd); *p; ++p, ++p1) {
301 if (*p == '"' || *p == '//')
302 *p1++ = '//';
303 *p1 = *p;
304 }
305 *p1++ = '"';
306 *p1++ = '/0';
307 if (p1 - tmpcmd > sizeof(tmpcmd)) {
308 error("tmpcmd overflow, should never happen");
309 exit(1);
310 }
311 if ((fin = popen(tmpcmd, "r")) == NULL) {
312 error("Can't execute: %s", tmpcmd);
313 return -1;
314 }
315 /* else */
316
317 /*
318 * Collect the result
319 */
320 while (fgets(tmpcmd, PATH_MAX, fin) != NULL) {
321 int l = strlen(tmpcmd);
322
323 line = (char *)xrealloc(line, len + l + 1);
324 line[len] = '/0';
325 strcat(line + len, tmpcmd); /* safe, realloc */
326 len += l;
327 }
328 pclose(fin);
329
330 if (line) {
331 /* shell used to strip one set of quotes. Paranoia code in
332 * 2.3.20 stops that strip so we do it ourselves.
333 */
334 split_line(g, line, 1);
335 free(line);
336 }
337
338 return 0;
339 }
在1301行中调用meta_expand的type为ME_ALL,该宏定义在./modutils-2.4.0/include/util.h中。
76 #define ME_ALL (ME_GLOB|ME_SHELL_COMMAND|ME_BUILTIN_COMMAND)
至此,进入174行的if块中。178行的for循环提取”`”包含的内容,这里是uname –r。在182~193行中,uname –r被替换为version信息。195~215行则对kernelversion进行替换,替换部分为version的x.xx,也就是主次版本号。224行以下,对Shell元符号进行处理。这里我们将进入224行的if块。在这里加上base_dir,也就是path的起始目录路径,在insmod中,base_dir为null。231~237处理分行的情况——将他们拷贝到一处。