Update changelog
[l2tpns.git] / nsctl.c
1 /* l2tpns plugin control */
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6 #include <errno.h>
7 #include <string.h>
8 #include <netdb.h>
9 #include <signal.h>
10 #include <sys/socket.h>
11 #include <linux/rtnetlink.h>
12
13 #include "dhcp6.h"
14 #include "l2tpns.h"
15 #include "control.h"
16
17 struct {
18 char *command;
19 char *usage;
20 int action;
21 } builtins[] = {
22 { "load_plugin", " PLUGIN Load named plugin", NSCTL_REQ_LOAD },
23 { "unload_plugin", " PLUGIN Unload named plugin", NSCTL_REQ_UNLOAD },
24 { "help", " List available commands", NSCTL_REQ_HELP },
25 { 0 }
26 };
27
28 static int debug = 0;
29 static int timeout = 2; // 2 seconds
30 static char *me;
31
32 #define USAGE() fprintf(stderr, "Usage: %s [-d] [-h HOST[:PORT]] [-t TIMEOUT] COMMAND [ARG ...]\n", me)
33
34 static struct nsctl *request(char *host, int port, int type, int argc, char *argv[]);
35
36 int main(int argc, char *argv[])
37 {
38 int req_type = 0;
39 char *host = 0;
40 int port;
41 int i;
42 char *p;
43 struct nsctl *res;
44
45 if ((p = strrchr((me = argv[0]), '/')))
46 me = p + 1;
47
48 opterr = 0;
49 while ((i = getopt(argc, argv, "dh:t:")) != -1)
50 switch (i)
51 {
52 case 'd':
53 debug++;
54 break;
55
56 case 'h':
57 host = optarg;
58 break;
59
60 case 't':
61 timeout = atoi(optarg);
62 break;
63
64 default:
65 USAGE();
66 return EXIT_FAILURE;
67 }
68
69 argc -= optind;
70 argv += optind;
71
72 if (argc < 1 || !argv[0][0])
73 {
74 USAGE();
75 return EXIT_FAILURE;
76 }
77
78 if (!host)
79 host = "127.0.0.1";
80
81 if ((p = strchr(host, ':')))
82 {
83 port = atoi(p + 1);
84 if (!port)
85 {
86 fprintf(stderr, "%s: invalid port `%s'\n", me, p + 1);
87 return EXIT_FAILURE;
88 }
89
90 *p = 0;
91 }
92 else
93 {
94 port = NSCTL_PORT;
95 }
96
97 for (i = 0; !req_type && builtins[i].command; i++)
98 if (!strcmp(argv[0], builtins[i].command))
99 req_type = builtins[i].action;
100
101 if (req_type == NSCTL_REQ_HELP)
102 {
103 printf("Available commands:\n");
104 for (i = 0; builtins[i].command; i++)
105 printf(" %s%s\n", builtins[i].command, builtins[i].usage);
106 }
107
108 if (req_type)
109 {
110 argc--;
111 argv++;
112 }
113 else
114 {
115 req_type = NSCTL_REQ_CONTROL;
116 }
117
118 if ((res = request(host, port, req_type, argc, argv)))
119 {
120 FILE *stream = stderr;
121 int status = EXIT_FAILURE;
122
123 if (res->type == NSCTL_RES_OK)
124 {
125 stream = stdout;
126 status = EXIT_SUCCESS;
127 }
128
129 for (i = 0; i < res->argc; i++)
130 fprintf(stream, "%s\n", res->argv[i]);
131
132 return status;
133 }
134
135 return EXIT_FAILURE;
136 }
137
138 static void sigalrm_handler(int sig) { }
139
140 static struct nsctl *request(char *host, int port, int type, int argc, char *argv[])
141 {
142 static struct nsctl res;
143 struct sockaddr_in peer;
144 socklen_t len = sizeof(peer);
145 struct hostent *h = gethostbyname(host);
146 int fd;
147 uint8_t buf[NSCTL_MAX_PKT_SZ];
148 int sz;
149 char *err;
150
151 if (!h || h->h_addrtype != AF_INET)
152 {
153 fprintf(stderr, "%s: invalid host `%s'\n", me, host);
154 return 0;
155 }
156
157 if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
158 {
159 fprintf(stderr, "%s: can't create udp socket (%s)\n", me, strerror(errno));
160 return 0;
161 }
162
163 memset(&peer, 0, len);
164 peer.sin_family = AF_INET;
165 peer.sin_port = htons(port);
166 memcpy(&peer.sin_addr.s_addr, h->h_addr, sizeof(peer.sin_addr.s_addr));
167
168 if (connect(fd, (struct sockaddr *) &peer, sizeof(peer)) < 0)
169 {
170 fprintf(stderr, "%s: udp connect failed (%s)\n", me, strerror(errno));
171 return 0;
172 }
173
174 if ((sz = pack_control(buf, sizeof(buf), type, argc, argv)) < 0)
175 {
176 fprintf(stderr, "%s: error packing request\n", me);
177 return 0;
178 }
179
180 if (debug)
181 {
182 struct nsctl req;
183 if (unpack_control(&req, buf, sz) == type)
184 {
185 fprintf(stderr, "Sending ");
186 dump_control(&req, stderr);
187 }
188 }
189
190 if (send(fd, buf, sz, 0) < 0)
191 {
192 fprintf(stderr, "%s: error sending request (%s)\n", me, strerror(errno));
193 return 0;
194 }
195
196 /* set timer */
197 if (timeout)
198 {
199 struct sigaction alrm;
200 alrm.sa_handler = sigalrm_handler;
201 sigemptyset(&alrm.sa_mask);
202 alrm.sa_flags = 0;
203
204 sigaction(SIGALRM, &alrm, 0);
205 alarm(timeout);
206 }
207
208 sz = recv(fd, buf, sizeof(buf), 0);
209 alarm(0);
210
211 if (sz < 0)
212 {
213 fprintf(stderr, "%s: error receiving response (%s)\n", me,
214 errno == EINTR ? "timed out" : strerror(errno));
215
216 return 0;
217 }
218
219 if ((type = unpack_control(&res, buf, sz)) > 0 && type & NSCTL_RESPONSE)
220 {
221 if (debug)
222 {
223 fprintf(stderr, "Received ");
224 dump_control(&res, stderr);
225 }
226
227 return &res;
228 }
229
230 err = "unknown error";
231 switch (type)
232 {
233 case NSCTL_ERR_SHORT: err = "short packet"; break;
234 case NSCTL_ERR_LONG: err = "extra data"; break;
235 case NSCTL_ERR_MAGIC: err = "bad magic"; break;
236 case NSCTL_ERR_TYPE: err = "invalid type"; break;
237 }
238
239 fprintf(stderr, "%s: %s\n", me, err);
240 return 0;
241 }