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