Added AUTHORS and COPYING content
[guacamole.git] / proxy / daemon.c
1
2 /*
3  *  Guacamole - Clientless Remote Desktop
4  *  Copyright (C) 2010  Michael Jumper
5  *
6  *  This program is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU Affero General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU Affero General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Affero General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <unistd.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <dlfcn.h>
28 #include <pthread.h>
29
30 #include <errno.h>
31 #include <syslog.h>
32
33 #include "client.h"
34
35 typedef struct client_thread_data {
36
37     int fd;
38     guac_client_init_handler* client_init;
39     guac_client_registry* registry;
40
41     int argc;
42     char** argv;
43
44 } client_thread_data;
45
46
47 void* start_client_thread(void* data) {
48
49     guac_client* client;
50     client_thread_data* thread_data = (client_thread_data*) data;
51
52     syslog(LOG_INFO, "Spawning client");
53
54     /* Load and start client */
55     client = guac_get_client(thread_data->fd, thread_data->registry, thread_data->client_init, thread_data->argc, thread_data->argv); 
56
57     if (client == NULL) {
58         syslog(LOG_ERR, "Client retrieval failed");
59         return NULL;
60     }
61
62     guac_start_client(client);
63
64     /* FIXME: Need to free client, but only if the client is not
65      * being used. This line will be reached if handoff occurs
66      */
67     guac_free_client(client, thread_data->registry);
68
69     /* Close socket */
70     if (close(thread_data->fd) < 0) {
71         syslog(LOG_ERR, "Error closing connection: %s", strerror(errno));
72         free(data);
73         return NULL;
74     }
75
76     syslog(LOG_INFO, "Client finished");
77     free(data);
78     return NULL;
79
80 }
81
82 int main(int argc, char* argv[]) {
83
84     /* Client registry */
85     guac_client_registry* registry;
86
87     /* Pluggable client */
88     void* client_plugin_handle;
89
90     union {
91         guac_client_init_handler* client_init;
92         void* obj;
93     } alias;
94
95     char* error;
96
97     /* Server */
98     int socket_fd;
99     struct sockaddr_in server_addr;
100
101     /* Client */
102     struct sockaddr_in client_addr;
103     unsigned int client_addr_len;
104     int connected_socket_fd;
105
106     int listen_port;
107
108     int client_argc;
109     char** client_argv;
110
111     char protocol_lib[256] = "libguac_client_";
112
113     if (argc < 3) {
114         fprintf(stderr, "USAGE: %s LISTENPORT PROTOCOL [PROTOCOL OPTIONS]\n", argv[0]);
115         return 1;
116     }
117
118     listen_port = atoi(argv[1]);
119     strcat(protocol_lib, argv[2]);
120     strcat(protocol_lib, ".so");
121
122     client_argc = argc - 3;
123     client_argv = &(argv[3]);
124
125     /* Get binding address */
126     memset(&server_addr, 0, sizeof(server_addr)); /* Zero struct */
127     server_addr.sin_family = AF_INET;
128     server_addr.sin_addr.s_addr = INADDR_ANY;
129     server_addr.sin_port = htons(listen_port);
130
131     /* Get socket */
132     socket_fd = socket(AF_INET, SOCK_STREAM, 0);
133     if (socket_fd < 0) {
134         syslog(LOG_ERR, "Error opening socket: %s", strerror(errno));
135         return 1;
136     }
137
138     /* Bind socket to address */
139     if (bind(socket_fd, (struct sockaddr*) &server_addr,
140                 sizeof(server_addr)) < 0) {
141         syslog(LOG_ERR, "Error binding socket: %s", strerror(errno));
142         return 2;
143     } 
144
145     syslog(LOG_INFO, "Loading client plugin");
146
147     /* Load client plugin */
148     client_plugin_handle = dlopen(protocol_lib, RTLD_LAZY);
149     if (!client_plugin_handle) {
150         syslog(LOG_ERR, "Could not open client plugin: %s", dlerror());
151         return 2;
152     }
153
154     dlerror(); /* Clear errors */
155
156     /* Get init function */
157     alias.obj = dlsym(client_plugin_handle, "guac_client_init");
158
159     if ((error = dlerror()) != NULL) {
160         syslog(LOG_ERR, "Could not get guac_client_init in  plugin: %s", error);
161         return 2;
162     }
163
164
165     syslog(LOG_INFO, "Listening on port %i", listen_port);
166
167
168     /* Allocate registry */
169     registry = guac_create_client_registry();
170
171     /* Daemon loop */
172     for (;;) {
173
174         pthread_t thread;
175         client_thread_data* data;
176
177         /* Listen for connections */
178         if (listen(socket_fd, 5) < 0) {
179             syslog(LOG_ERR, "Could not listen on socket: %s", strerror(errno));
180             return 3;
181         }
182
183         /* Accept connection */
184         client_addr_len = sizeof(client_addr);
185         connected_socket_fd = accept(socket_fd, (struct sockaddr*) &client_addr, &client_addr_len);
186         if (connected_socket_fd < 0) {
187             syslog(LOG_ERR, "Could not accept client connection: %s", strerror(errno));
188             return 3;
189         }
190
191         data = malloc(sizeof(client_thread_data));
192
193         data->fd = connected_socket_fd;
194         data->client_init = alias.client_init;
195         data->registry = registry;
196         data->argc = client_argc;
197         data->argv = client_argv;
198
199         if (pthread_create(&thread, NULL, start_client_thread, (void*) data)) {
200             syslog(LOG_ERR, "Could not create client thread: %s", strerror(errno));
201             return 3;
202         }
203
204     }
205
206     /* FIXME: Cleanup client registry (and all other objects) on exit */
207
208     /* Close socket */
209     if (close(socket_fd) < 0) {
210         syslog(LOG_ERR, "Could not close socket: %s", strerror(errno));
211         return 3;
212     }
213
214     /* Load client plugin */
215     if (dlclose(client_plugin_handle)) {
216         syslog(LOG_ERR, "Could not close client plugin: %s", dlerror());
217         return 2;
218     }
219
220
221     return 0;
222
223 }
224