Merge branch 'blah'
[munin-test.git] / munin-node.c
blobc3557afe03868ff6a983d5db3fb1fc2ca362f303
1 /* A complicated in the internet domain using TCP */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <sys/types.h>
5 #include <sys/socket.h>
6 #include <sys/stat.h>
7 #include <netinet/in.h>
8 #include <signal.h>
9 #include <string.h>
10 #include <sys/time.h>
12 #include <dirent.h>
13 #include <unistd.h>
14 #include <errno.h>
15 #include <regex.h>
17 #define MAX_DIR_STR 4096
18 #define CONFIGEXPR "^config (([a-zA-z0-9_]+(\\.)?)+)"
19 #define FETCHEXPR "^fetch (([a-zA-z0-9_]+(\\.)?)+)"
20 #define LISTEXPR "^list"
21 #define QUITEXPR "^quit"
23 void commands(int);
25 /* This function is called when a system call fails,
26 * it will display on stderr and then abort */
27 void error(char *msg)
29 perror(msg);
30 exit(1);
33 int main()
35 /* The following keeps zombies at bay.
36 * When children die and the parent doesn't wait() on
37 * them then their SIGCHLD handler is not happy because
38 * the process is not permitted to fully die because at
39 * some point in the future, the parent of the process might
40 * want to execute a wait and would want info on the death of
41 * the child. */
43 signal(SIGCHLD,SIG_IGN);
45 int sockfd, newsockfd, portno, clilen, pid;
46 portno = 4949;
48 /* sockfd and newsockfd are file descriptors containing
49 * the values returned by the socket and accept syscall
50 * portno stores the port number the server will accept
51 * connections, clilen stores the size of the address of
52 * the client, this is needed for the accept syscall,
53 * n is the return value for the read() and write() calls,
54 * which will be the number of characters read or written.
57 /* serv_addr will contain the address of the server, and
58 * cli_addr will contain the address of the client connecting */
60 struct sockaddr_in serv_addr, cli_addr;
62 /* Create a new socket */
63 sockfd = socket(AF_INET, SOCK_STREAM, 0);
64 if (sockfd < 0)
65 error("ERROR opening socket");
67 u_int yes = 1;
68 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
69 error("can't reuse, lame.");
70 return;
73 /* zero out the serv_addr buffer */
75 bzero((char *) &serv_addr, sizeof(serv_addr));
77 /* set serv_addr structure fields:
78 * sin_family = code for the address family
79 * sin_port = contains the port number (converted to network byte order)
80 * in_addr = contains s_addr = IP address of the server */
81 serv_addr.sin_family = AF_INET;
82 serv_addr.sin_port = htons(portno);
83 serv_addr.sin_addr.s_addr = INADDR_ANY;
85 /* bind() binds a socket to an address, takes socket fd, address to bind
86 * size of the address to which it is bound */
87 if (bind(sockfd, (struct sockaddr *) &serv_addr,
88 sizeof(serv_addr)) < 0)
89 error("ERROR on binding");
91 /* listen on the socket for connections */
92 listen(sockfd,5);
94 /* accept() causes the process to block until client connects, wakes up
95 * when a connection from a client has succeeded and then returns a new
96 * file descriptor, and all communications should be done on this fd.
97 * The second argument is a reference pointer to the address of the client
98 * on the other end of the connection, third arg is the size of the structure. */
99 clilen = sizeof(cli_addr);
101 while (1) {
102 newsockfd = accept(sockfd,
103 (struct sockaddr *) &cli_addr,
104 &clilen);
105 if (newsockfd < 0)
106 error("ERROR on accept");
107 pid = fork();
108 if (pid <0)
109 error("ERROR on fork");
110 if (pid == 0) {
111 close (sockfd);
112 commands(newsockfd);
113 exit(0);
115 else close (newsockfd);
116 } /* end of while loop */
117 return 0; /* never gets here */
120 /******** commands() *********************
121 * There is a separate instance of this function
122 * for each connection. It handles all communication
123 * once a connnection has been established.
124 ******************************************/
126 void commands(int sock)
128 int n;
130 /* Server reads characters from the socket connection into
131 * this buffer */
132 char buffer[256];
134 char* nodemsg;
135 nodemsg = "birdbath.riseup.net\n.\n";
137 char* version;
138 version = "fakemunins node on birdbath.riseup.net version: 1.2.4\n";
140 char* timeoutmsg;
141 timeoutmsg = "Timeout!\n";
143 char* errormsg;
144 errormsg = "# Unknown command. Try list, nodes, config, fetch, version or quit\n";
146 char* greeting;
147 greeting = "# munin node at birdbath.riseup.net\n";
149 char* unknownservice;
150 unknownservice = "# Unknown service\n.\n";
152 char* debug;
153 debug = "# HI YOU GOT TEH DEBEUG!!?!\n";
155 char* fetch;
156 fetch = "# fetch message\n";
158 char* eofmsg;
159 eofmsg = ".\n";
161 n = write(sock, greeting, strlen(greeting));
163 int loop;
164 loop=1;
166 /* To be able to match regular expressions, we need this,
167 * one for "config" and one for "fetch"
170 regex_t config_re;
171 if(regcomp(&config_re, CONFIGEXPR, REG_EXTENDED) != 0) {
172 exit (1);
175 regex_t fetch_re;
176 if(regcomp(&fetch_re, FETCHEXPR, REG_EXTENDED) != 0) {
177 exit (1);
180 regex_t list_re;
181 if(regcomp(&list_re, LISTEXPR, REG_EXTENDED) != 0) {
182 exit (1);
185 regex_t quit_re;
186 if(regcomp(&quit_re, QUITEXPR, REG_EXTENDED) != 0) {
187 exit (1);
190 char ret[MAX_DIR_STR];
191 char* dir;
193 while (loop) {
195 bzero(buffer,256);
196 n = read(sock,buffer,255);
197 buffer[n+1] = '\0';
199 if (n < 0) error("ERROR reading from socket");
201 if((regexec(&list_re, buffer, 0, NULL, 0)) == 0) {
202 struct dirent *dp;
203 DIR *dfd;
204 ret[0] = 0;
205 char* list;
207 if ((dfd = opendir("/etc/munin/plugins")) == NULL) {
208 perror(dir);
209 return;
212 while ((dp = readdir(dfd)) !=NULL) {
214 /* skip self and parent */
215 if (strcmp(dp->d_name, ".") == 0 ||
216 strcmp(dp->d_name, "..") == 0) {
217 continue;
220 if (strlen(ret) + strlen(dp->d_name) + 2 > MAX_DIR_STR) {
221 closedir(dfd);
222 strcpy(ret, "too long");
223 return;
224 } else {
225 strcat(ret, strcat(dp->d_name, " "));
228 ret[strlen(ret) - 1] = 0;
229 closedir(dfd);
230 strcat(ret, "\n");
231 n = write (sock, ret, strlen(ret));
235 else if(strcmp(buffer, "nodes\r\n") == 0) {
236 n = write (sock, nodemsg, strlen(nodemsg));
239 /* do a a regexp to see if "config bar" was sent
240 * check if second arg is a plugin, otherwise print
241 * "# Unknown service\n." */
243 else if((regexec(&config_re, buffer, 0, NULL, 0)) == 0) {
244 struct stat statinfo;
245 char *p = strchr(buffer, ' '); /* Now p will point to the space between config and bar */
246 char *service = p+1; /* put whatever is after that space into *service */
247 char *servicedir = malloc(sizeof(char) * 1024); /* allocate some memory to store everything */
248 char *plugin_path = "/etc/munin/plugins"; /* where the plugins are located */
250 /* Do a little overflow checking */
251 if ((strlen(plugin_path) + strlen(service) + 3) > 1024) {
252 error("path too long");
253 return;
256 /* Prepend the plugin path into the beginning of the service name */
257 sprintf(servicedir, "%s/%s", plugin_path, service);
259 /* Remove the \r\n at the end of the string */
260 if (servicedir[strlen(servicedir) -1 ] == '\n') {
261 if (servicedir[strlen(servicedir) -2 ] == '\r') {
262 servicedir[strlen(servicedir) - 2] = 0;
264 else {
265 servicedir[strlen(servicedir) - 1] = 0;
269 /* what the string has
270 char*temp = servicedir; while(*temp){printf("%c - %d\n", *temp, (int)*temp);temp++;}
273 /* Check to see if the service entered exists in the directory */
274 if(stat(servicedir, &statinfo) == -1) {
275 n = write(sock, unknownservice, strlen(unknownservice));
277 /* Since it does exist, check to make sure it is a regular file or a link */
278 else if(statinfo.st_mode & (S_IFREG || S_IFLNK)) {
279 FILE *fpipe;
280 char results[256];
281 char *command=(strcat(servicedir, " config"));
283 if ( !(fpipe = (FILE*)popen(command,"r")) )
284 { // If fpipe is NULL
285 perror("Problems with pipe");
286 exit(1);
289 while ( fgets( results, sizeof results, fpipe))
291 n = write(sock, results, strlen(results));
293 pclose(fpipe);
294 n = write(sock, eofmsg, strlen(eofmsg));
296 /* gotta free up this memory because servicedir is malloc()'d */
297 free(servicedir);
300 /* do a a regexp to see if "fetch bar" was sent
301 * check if second arg is a plugin, otherwise print
302 * "# Unknown service\n." */
304 else if((regexec(&fetch_re, buffer, 0, NULL, 0)) == 0) {
305 struct stat statinfo;
306 char *p = strchr(buffer, ' '); /* Now p will point to the space between config and bar */
307 char *service = p+1; /* put whatever is after that space into *service */
308 char *servicedir = malloc(sizeof(char) * 1024); /* allocate some memory to store everything */
309 char *plugin_path = "/etc/munin/plugins"; /* where the plugins are located */
311 /* Do a little overflow checking */
312 if ((strlen(plugin_path) + strlen(service) + 3) > 1024) {
313 error("path too long");
314 return;
317 /* Prepend the plugin path into the beginning of the service name */
318 sprintf(servicedir, "%s/%s", plugin_path, service);
320 /* Remove the \r\n at the end of the string */
321 if (servicedir[strlen(servicedir) -1 ] == '\n') {
322 if (servicedir[strlen(servicedir) -2 ] == '\r') {
323 servicedir[strlen(servicedir) - 2] = 0;
325 else {
326 servicedir[strlen(servicedir) - 1] = 0;
330 /* Check to see if the service entered exists in the directory */
331 if(stat(servicedir, &statinfo) == -1) {
332 n = write(sock, unknownservice, strlen(unknownservice));
334 /* Since it does exist, check to make sure it is a regular file or a link */
335 else if(statinfo.st_mode & (S_IFREG || S_IFLNK)) {
336 FILE *fpipe;
337 char results[256];
338 char *command=(strcat(servicedir, " fetch"));
340 if ( !(fpipe = (FILE*)popen(command,"r")) )
341 { // If fpipe is NULL
342 perror("Problems with pipe");
343 exit(1);
346 while ( fgets( results, sizeof results, fpipe))
348 n = write(sock, results, strlen(results));
350 pclose(fpipe);
351 n = write(sock, eofmsg, strlen(eofmsg));
353 /* gotta free up this memory because servicedir is malloc()'d */
354 free(servicedir);
357 else if(strcmp(buffer, "help\r\n") == 0) {
358 n = write (sock, errormsg, strlen(errormsg));
361 else if(strcmp(buffer, "version\r\n") == 0) {
362 n = write (sock, version, strlen(version));
365 else if((regexec(&quit_re, buffer, 0, NULL, 0)) == 0) {
366 exit(0);
369 else {
370 n = write(sock, errormsg, strlen(errormsg));
374 if (n < 0) error("ERROR writing to socket");