Busybox 框架代码分析
对Busybox 1.1.2版的大体框架进行了抽取,以便于看清。
1 /* 2 Author: 3 Framework of busybox 4 5 A simple demo to show how busybox works 6 7 complie: 8 gcc ***.c -o busybox 9 10 run: 11 ./busybox bbtest 12 13 */ 14 15 #include <stdlib.h> 16 #include <stdarg.h> 17 #include <errno.h> 18 #include <string.h> 19 #include <stdio.h> 20 21 /* 22 包含名字和入口地址函数的结构体 23 */ 24 struct BB_applet 25 { 26 const char* name; 27 int (*main) (int argc, char** argv); 28 }; 29 30 int bbtest_main(int argc, char* argv[]); 31 void bb_error_msg_and_die(const char *s, ...); 32 void run_applet_by_name(const char* name,int argc,char* argv[]); 33 static struct BB_applet* applet_using; 34 char* bb_applet_name = NULL; 35 /* 36 结构体数组 37 */ 38 const struct BB_applet applets[] = 39 { 40 #define APPLET(a,b) {#a,b} 41 APPLET(bbtest, bbtest_main) 42 }; 43 44 const size_t NUM_APPLETS = (sizeof (applets) / sizeof (struct BB_applet)); 45 46 int main(int argc, char** argv) 47 { 48 const char* s; 49 50 bb_applet_name = argv[0]; 51 /* 52 if (*bb_applet_name == '-') bb_applet_name++; 53 */ 54 /* 55 对类似于 “./busybox vi” 56 的命令进行解析 57 */ 58 for (s = bb_applet_name; *s ;) 59 if (*(s++) == '/') bb_applet_name = s; 60 61 /* Set locale for everybody except `init' */ 62 /* 63 if(ENABLE_LOCALE_SUPPORT && getpid() != 1) 64 setlocale(LC_ALL, ""); 65 */ 66 67 printf("%s\n",bb_applet_name); 68 /* 69 按名字找到对应的组件程序,并执行 70 */ 71 run_applet_by_name(bb_applet_name, argc, argv); 72 /* 73 bb_error_msg_and_die("applet not found"); 74 */ 75 } 76 77 78 int busybox_main(int argc, char** argv) 79 { 80 /* 81 * This style of argument parsing doesn't scale well 82 * in the event that busybox starts wanting more --options. 83 * If someone has a cleaner approach, by all means implement it. 84 */ 85 /* 86 if (ENABLE_FEATURE_INSTALLER && argc > 1 && !strcmp(argv[1], "--install")) { 87 int use_symbolic_links = 0; 88 int rc = 0; 89 char *busybox; 90 */ 91 92 /* to use symlinks, or not to use symlinks... */ 93 /* 94 if (argc > 2) { 95 if ((strcmp(argv[2], "-s") == 0)) { 96 use_symbolic_links = 1; 97 } 98 } 99 */ 100 101 /* link */ 102 /* 103 busybox = xreadlink("/proc/self/exe"); 104 if (busybox) { 105 install_links(busybox, use_symbolic_links); 106 free(busybox); 107 } else { 108 rc = 1; 109 } 110 return rc; 111 } 112 */ 113 114 /* Deal with --help. (Also print help when called with no arguments) */ 115 116 if (argc == 1 || !strcmp(argv[1], "--help") ) 117 { 118 if (argc > 2) 119 { 120 run_applet_by_name(bb_applet_name = argv[2], 2, argv); 121 } 122 else 123 { 124 const struct BB_applet* a; 125 int col, output_width; 126 127 /* 128 if (ENABLE_FEATURE_AUTOWIDTH) 129 { 130 */ 131 /* Obtain the terminal width. */ 132 /* 133 get_terminal_width_height(0, &output_width, NULL); 134 */ 135 /* leading tab and room to wrap */ 136 /* 137 output_width -= 20; 138 } 139 else output_width = 60; 140 */ 141 142 143 printf(/*"%s\n\n"*/ 144 "Usage: busybox [function] [arguments]...\n" 145 " or: [function] [arguments]...\n\n" 146 "\tBusyBox is a multi-call binary that combines many common Unix\n" 147 "\tutilities into a single executable. Most people will create a\n" 148 "\tlink to busybox for each function they wish to use and BusyBox\n" 149 "\twill act like whatever it was invoked as!\n" 150 "\nCurrently defined functions:\n"/*, bb_msg_full_version*/); 151 col = 0; 152 for (a = applets; a->name;) 153 { 154 col += printf("%s%s", (col ? ", " : "\t"), (a++)->name); 155 if (col > output_width && a->name) 156 { 157 printf(",\n"); 158 col = 0; 159 } 160 } 161 162 printf("\n\n"); 163 exit(0); 164 } 165 } 166 else run_applet_by_name(bb_applet_name = argv[1], argc - 1, argv + 1); 167 168 bb_error_msg_and_die("applet not found"); 169 } 170 171 struct BB_applet* find_applet_by_name (const char* name); 172 void run_applet_by_name (const char* name, int argc, char** argv) 173 { 174 /* 175 if(ENABLE_FEATURE_SUID_CONFIG) parse_config_file (); 176 */ 177 /* 178 如果命令是“./busybox vi”形式的 179 */ 180 if (!strncmp(name, "busybox", 7)) busybox_main(argc, argv); 181 /* Do a binary search to find the applet entry given the name. */ 182 applet_using = find_applet_by_name(name); 183 if (applet_using) 184 { 185 bb_applet_name = applet_using->name; 186 /*if (argc == 2 && !strcmp(argv[1], "--help")) bb_show_usage ();*/ 187 /* if(ENABLE_FEATURE_SUID) check_suid (applet_using);*/ 188 /* 189 跳转到对应的组件程序执行 190 */ 191 exit ((*(applet_using->main)) (argc, argv)); 192 } 193 } 194 195 /* 196 比较组件程序名字的回调函数 197 */ 198 static int applet_name_compare (const void* x, const void* y) 199 { 200 const char* name = x; 201 const struct BB_applet* applet = y; 202 203 return strcmp (name, applet->name); 204 } 205 206 /* 207 按名字进行二分查找 208 这也是applet.h 的head中一直强调要按字符顺序来添加组件入口的原因 209 后续版本的busybox中已经做了修改,会对组件结构体进行qsort排序 210 */ 211 struct BB_applet* find_applet_by_name (const char* name) 212 { 213 return bsearch (name, applets, NUM_APPLETS, sizeof (struct BB_applet), 214 applet_name_compare); 215 } 216 217 /* 218 作为测试组件的main函数 219 */ 220 int bbtest_main(int argc, char* argv[]) 221 { 222 printf("/***********************************************************/\n" 223 " Now in bbtest!\n"); 224 return 0; 225 } 226 227 228 void bb_verror_msg(const char *s, va_list p) 229 { 230 fflush(stdout); 231 fprintf(stderr, "%s: ", bb_applet_name); 232 vfprintf(stderr, s, p); 233 } 234 235 int bb_default_error_retval=EXIT_FAILURE; 236 void bb_error_msg_and_die(const char *s, ...) 237 { 238 va_list p; 239 240 va_start(p, s); 241 bb_verror_msg(s, p); 242 va_end(p); 243 putc('\n', stderr); 244 exit(bb_default_error_retval); 245 }
阅读的时候遇到下面这段代码里的类似USE_TEST,USE_ADDGROUP之类的宏定义一直不理解。
这些宏定义只有在applets.h里面出现过一次,在所有的源文件中找不到其定义,按常理是不会通过编译的。
后来问了寝室里的大师兄才了解到,编译的时候会自动生成bb_config.h这个头文件。里面定义了这些宏定义,其实就是直接脱掉了外层的宏展开。
(编译时提示“procps.c:15:22: error: asm/page.h: No such find.”的解决方法)
这种在编译的最开始时才生成头文件的技术是第一次遇到,果然见识还是太短了。
1 const struct BB_applet applets[] = { 2 # define APPLET(a,b,c,d) {#a,b,c,d}, 3 # define APPLET_NOUSAGE(a,b,c,d) {#a,b,c,d}, 4 # define APPLET_ODDNAME(a,b,c,d,e) {#a,b,c,d}, 5 #endif 6 7 #ifdef CONFIG_INSTALL_NO_USR 8 # define _BB_DIR_USR_BIN _BB_DIR_BIN 9 # define _BB_DIR_USR_SBIN _BB_DIR_SBIN 10 #endif 11 12 13 USE_TEST(APPLET_NOUSAGE([, test_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 14 USE_TEST(APPLET_NOUSAGE([[, test_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 15 USE_ADDGROUP(APPLET(addgroup, addgroup_main, _BB_DIR_BIN, _BB_SUID_NEVER)) 16 /* 17 ............. 18 */ 19 };