上一期说了initdb的大概流程和初期准备,这一期说一说参数解析
首先看一下initdb的入口,main函数
main(int argc, char *argv[])
这里有两个参数,argc和argv[]
argc: 命令行总的参数个数
argv[]:保存命令行参数的字符串指针,其中argv[0]参数是程序的全名
假设我在命令行输入initdb -D pgdata
argc=3
argv[0]=“F:\postgresql-9.6.10\bin\bin\initdb.exe”
argv[1]=“-D“
argv[2]=“pgdata”
首先要解析的是调用程序的实际名称
progname = get_progname(argv[0]);
const char *
get_progname(const char *argv0)
{
const char *nodir_name;
char *progname;
//这里从F:\postgresql-9.6.10\bin\bin\initdb.exe得到initdb.exe
nodir_name = last_dir_separator(argv0);
if (nodir_name)
nodir_name++;
else
nodir_name = skip_drive(argv0);
/*
* Make a copy in case argv[0] is modified by ps_status. Leaks memory, but
* called only once.
*/
progname = strdup(nodir_name);
if (progname == NULL)
{
fprintf(stderr, "%s: out of memory\n", nodir_name);
abort(); /* This could exit the postmaster */
}
#if defined(__CYGWIN__) || defined(WIN32)
//如果是windows平台,去掉.exe.的后缀
/* strip ".exe" suffix, regardless of case */
if (strlen(progname) > sizeof(EXE) - 1 &&
pg_strcasecmp(progname + strlen(progname) - (sizeof(EXE) - 1), EXE) == 0)
progname[strlen(progname) - (sizeof(EXE) - 1)] = '\0';
#endif
//最终返回调用程序的实际名称initdb
return progname;
}
接下来是解析参数,首先判断参数是否是版本或者帮助信息
//刚刚上面分析过,argc = 3,argv[1] = “-D”,所以不进这个if
if (argc > 1)
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
usage(progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
{
puts("initdb (PostgreSQL) " PG_VERSION);
exit(0);
}
}
接下来就是正儿八经的参数解析了,可解析参数类型很多,完整的参数列表可以看官方手册或者我上一篇的文章,这里用-D举例
while ((c = getopt_long(argc, argv, "dD:E:kL:nNU:WA:sST:X:", long_options, &option_index)) != -1)
{
switch (c)
{
……
case 'D':
pg_data = pg_strdup(optarg);
break;
……
default:
/* getopt_long already emitted a complaint */
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),progname);
exit(1);
}
首先看getopt_long这个函数,入参有以下几个
argc:上面分析过,参数个数。
argv:上面分析过,命令行参数的字符串指针。
“dD:E:kL:nNU:WA:sST:X:”:可识别的字符选项。
long_options:参数列表,在上一篇文章中分析过。
&option_index:参数索引,标识当前在第几个参数。
getopt_long函数的返回值只是参数的内部value,方便后续操作识别,参数的值则是通过optarg这个全局变量传出来的。在getopt_long中optarg被赋值为F:\data,然后通过pg_strdup函数赋值给pgdata。其中pg_strdup函数是封装了strdup函数,在赋值前会先判断optarg是否为空。
后续还有一些异常处理,一般在一个参数后面添加多个值才会走到此路径
if (optind < argc)
{
fprintf(stderr, _("%s: too many command-line arguments (first is \"%s\")\n"),
progname, argv[optind]);
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
exit(1);
}
参数解析到这里就差不多结束了,在环境设定前还有一段代码,这里是数据同步的代码,只有在initdb时加上-S参数才会走到这里,这段代码会安全地把所有数据库文件写入到磁盘并退出。这不会执行任何正常的initdb操作。
/* If we only need to fsync, just do it and exit */
if (sync_only)
{
setup_pgdata();
/* must check that directory is readable */
if (pg_check_dir(pg_data) <= 0)
{
fprintf(stderr, _("%s: could not access directory \"%s\": %s\n"),
progname, pg_data, strerror(errno));
exit_nicely();
}
fsync_pgdata();
return 0;
}