Merge branch 'devel' of ssh://git.planet-lab.org/git/nodemanager into devel
[nodemanager.git] / forward_api_calls.c
1 /* 
2  * $Id$
3  * $URL$
4  *
5  * forward_api_calls.c: forward XMLRPC calls to the Node Manager
6  * Used as a shell, this code works in tandem with sshd
7  * to allow authenticated remote access to a localhost-only service.
8  *
9  * Bugs:
10  * Doesn't handle Unicode properly.  UTF-8 is probably OK.
11  *
12  * Change History:
13  * 2007/05/02: [deisenst] Increased buffer space to 1MiB.
14  *                        Increased TIMEOUT_SECS to 2min.
15  * 2006/10/30: [deisenst] Changed location of Unix socket.
16  * 2006/09/14: [deisenst] Switched to PF_UNIX sockets so that SO_PEERCRED works
17  * 2006/09/08: [deisenst] First version.
18  */
19
20 static const int TIMEOUT_SECS = 120;
21 const char *API_addr = "/tmp/nodemanager.api";
22
23 static const char *Header =
24   "POST / HTTP/1.0\r\n"
25   "Content-Type: text/xml\r\n"
26   "Content-Length: %d\r\n"
27   "\r\n%n";
28
29 static const char *Error_template =
30   "<?xml version='1.0'?>\n"
31   "<methodResponse>\n"
32   "<fault>\n"
33   "<value><struct>\n"
34   "<member>\n"
35   "<name>faultCode</name>\n"
36   "<value><int>1</int></value>\n"
37   "</member>\n"
38   "<member>\n"
39   "<name>faultString</name>\n"
40   "<value><string>%s: %s</string></value>\n"
41   "</member>\n"
42   "</struct></value>\n"
43   "</fault>\n"
44   "</methodResponse>\n";
45
46 #include <ctype.h>
47 #include <errno.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <sys/socket.h>
52 #include <sys/types.h>
53 #include <sys/un.h>
54 #include <unistd.h>
55
56 static void ERROR(const char *s) {
57   printf(Error_template, s, strerror(errno));
58   exit(1);
59 }
60
61 int main(int argc, char **argv, char **envp) {
62   ssize_t len;
63   char header_buf[1<<20];
64   char content_buf[1<<20];
65   size_t content_len;
66   int sockfd;
67   struct sockaddr_un addr;
68   int consecutive_newlines;
69
70   alarm(TIMEOUT_SECS);
71
72   /* read xmlrpc request from stdin
73    * 4 KiB ought to be enough for anyone
74    * 2007/05/02: [deisenst] It wasn't.
75    */
76   content_len = 0;
77   while(content_len < sizeof content_buf) {
78     len = read(0,
79                content_buf + content_len,
80                sizeof content_buf - content_len);
81     if(len < 0) ERROR("read()");
82     else if(0 == len) break;
83     content_len += len;
84   }
85
86   /* connect to the API server */
87   sockfd = socket(PF_UNIX, SOCK_STREAM, 0);
88   if(sockfd < 0)
89     ERROR("socket()");
90   memset(&addr, 0, sizeof addr);
91   addr.sun_family = AF_UNIX;
92   strncpy(addr.sun_path, API_addr, sizeof addr.sun_path);
93   if(connect(sockfd, (struct sockaddr *)&addr, sizeof addr) < 0)
94     ERROR("connect()");
95
96   /* send the request */
97   snprintf(header_buf, sizeof header_buf, Header, content_len, &len);
98   write(sockfd, header_buf, len);
99   write(sockfd, content_buf, content_len);
100   shutdown(sockfd, SHUT_WR);
101
102   /* forward the response */
103   consecutive_newlines = 0;
104   while((len = read(sockfd, content_buf, sizeof content_buf)) != 0) {
105     size_t processed_len = 0;
106     if(len < 0) {
107       /* "Connection reset by peer" is not worth bothering the user. */
108       if(ECONNRESET == errno) break;
109       else ERROR("read()");
110     }
111     content_len = len;
112
113     while(consecutive_newlines < 2 && processed_len < content_len) {
114       char ch = content_buf[processed_len++];
115       if(ch == '\n') consecutive_newlines++;
116       else if(!isspace(ch)) consecutive_newlines = 0;
117     }
118
119     if(processed_len < content_len) {
120       len = fwrite(content_buf + processed_len, sizeof (char),
121                    content_len - processed_len, stdout);
122       /* make sure faults don't mess up previously sent xml */
123       if(len < content_len - processed_len) ERROR("fwrite()");
124     }
125   }
126
127   /* goodbye */
128   shutdown(sockfd, SHUT_RD);
129   close(sockfd);
130
131   return 0;
132 }