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