源码解读
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "subcommands.h"
#include "version.h"
const char *PROG_NAME;
/*
这个静态数组,是wg的子命令列表,
char *subcommand,是子命令名称,也就是在wg后面跟着的第一个参数;
int (*function),函数指针,就是函数进入到内存后的地址信息,通过这个地址信息直接调用函数,因为是数组方式,所以每一项内容占用的内存大小一样。后面专门解释一下。
char *,对于该子命令的描述。
*/
static const struct {
const char *subcommand;
int (*function)(int, const char**);
const char *description;
} subcommands[] = {
{ "show", show_main, "Shows the current configuration and device information" },
{ "showconf", showconf_main, "Shows the current configuration of a given WireGuard interface, for use with `setconf'" },
{ "set", set_main, "Change the current configuration, add peers, remove peers, or change peers" },
{ "setconf", setconf_main, "Applies a configuration file to a WireGuard interface" },
{ "addconf", setconf_main, "Appends a configuration file to a WireGuard interface" },
{ "syncconf", setconf_main, "Synchronizes a configuration file to a WireGuard interface" },
{ "genkey", genkey_main, "Generates a new private key and writes it to stdout" },
{ "genpsk", genkey_main, "Generates a new preshared key and writes it to stdout" },
{ "pubkey", pubkey_main, "Reads a private key from stdin and writes a public key to stdout" }
};
static void show_usage(FILE *file)
{
fprintf(file, "Usage: %s <cmd> [<args>]\n\n", PROG_NAME);
fprintf(file, "Available subcommands:\n");
for (size_t i = 0; i < sizeof(subcommands) / sizeof(subcommands[0]); ++i)
fprintf(file, " %s: %s\n", subcommands[i].subcommand, subcommands[i].description);
fprintf(file, "You may pass `--help' to any of these subcommands to view usage.\n");
}
int main(int argc, const char *argv[])
{
/*
把输入的程序名称wg,先保留起来,主要用于后面输出提示信息用。
*/
PROG_NAME = argv[0];
/*
wg命令,如果只输入一个参数,并且这个参数是-v或--version或versiond,直接打印版本信息,return 0。
如果只输入一个参数,并且这个参数是-h或--help或help,展示语法信息,return 0。
如果不输入任何参数,则调用show_main()
如果输入多个参数,则需要对第一个参数进行检索,检索成功后,调用相应的函数指针,这个函数指针,并且把在命令行输入的参数进行剪裁后传给这个函数。
argc-1 就是传入的参数数量;argv+1,就是把原来命令行参数的指针往下走一个,这就是把输入的"wg"去掉后,重新传给了函数指针。
如果输入了多个参数,并且没有检索成功,则输出错误命令提示,return1。
*/
if (argc == 2 && (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version") || !strcmp(argv[1], "version"))) {
printf("wireguard-tools v%s - https://git.zx2c4.com/wireguard-tools/\n", WIREGUARD_TOOLS_VERSION);
return 0;
}
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "help"))) {
show_usage(stdout);
return 0;
}
if (argc == 1) {
static const char *new_argv[] = { "show", NULL };
return show_main(1, new_argv);
}
for (size_t i = 0; i < sizeof(subcommands) / sizeof(subcommands[0]); ++i) {
if (!strcmp(argv[1], subcommands[i].subcommand))
return subcommands[i].function(argc - 1, argv + 1);
}
fprintf(stderr, "Invalid subcommand: `%s'\n", argv[1]);
show_usage(stderr);
return 1;
}
备注:函数指针
关于函数指针的用法,本人认为是C语言的经典精华部分,在各大高性能软件中,都会看到类似的用法,尤其是在Nginx的源码中,更是用到极处。
比如在nginx源码中:typedef size_t (*ngx_log_handler_pt) (void *ctx, char *buf, size_t len);
举个最直接简单的例子吧,
#include <iostream>
using namespace std;
typedef int (*Pointer) (char, char);
int ss(char a, char b)
{
cout<<"in func ss()"<<endl;
cout<<a<<endl;
cout<<b<<endl;
return 0;
}
int bb(char a, char b)
{
cout<<"in func bb()"<<endl;
cout<<b<<endl;
cout<<a<<endl;
return 0;
}
int main()
{
Pointer p;
p = ss;
p('a','b');
p = bb;
p('a', 'b');
return 0;
}
通过p来调用两个函数。
运行结果:
in func ss()
a
b
in func bb()
b
a
当然了,这种函数指针,跟静态数组以及偏移量结合起来使用,不但能简洁源代码,并且能大大提升程序的运行效率。