1 /* vi:ai:et:ts=8 sw=2
2  */
3 /*
4  * wzdftpd - a modular and cool ftp server
5  * Copyright (C) 2002-2006  Pierre Chifflier
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  *
21  * As a special exemption, Pierre Chifflier
22  * and other respective copyright holders give permission to link this program
23  * with OpenSSL, and distribute the resulting executable, without including
24  * the source code for OpenSSL in the source distribution.
25  */
26 
27 #include <stdio.h>
28 #include <string.h>
29 
30 #if defined (WIN32)
31 #include <winsock2.h>
32 #include <ws2tcpip.h>
33 #include <process.h>
34 #include <windows.h>
35 #else
36 #include <sys/types.h>
37 #include <sys/wait.h>
38 #include <signal.h>
39 #include <arpa/inet.h> /* htonl() */
40 #include <unistd.h>    /* fork */
41 #endif
42 
43 #include <libwzd-core/wzd_structs.h>
44 #include <libwzd-core/wzd_log.h>
45 
46 #include <libwzd-core/wzd_configfile.h>
47 #include <libwzd-core/wzd_mod.h>
48 #include <libwzd-core/wzd_string.h>
49 
50 #ifndef WIN32
51 #include <pthread.h>
52 #endif
53 #include <libwzd-core/wzd_threads.h>
54 
55 /* use this define if you want to use
56    processes instead of threads. */
57 /* #define ZEROCONF_USE_PROCESS */
58 
59 /* function prototypes and globals */
60 #include "libwzd_zeroconf.h"
61 
62 /***********************/
63 MODULE_NAME(zeroconf);
64 MODULE_VERSION(101);
65 
66 static void * routine (void * arg);
67 
68 #ifdef ZEROCONF_USE_PROCESS
69 static pid_t pid_child = 0;
70 #else
71 static wzd_thread_t zeroconf_thread;
72 #endif
73 
74 static int initialized = 0;
75 
76 #ifdef USE_AVAHI
77 struct context *ctx = NULL;
78 #endif
79 
80 #ifdef ZEROCONF_USE_PROCESS
sighandler(int sig)81 static void sighandler(int sig)
82 {
83   out_log(LEVEL_FLOOD,"zeroconf: received signal %d\n",sig);
84 #ifdef USE_AVAHI
85   if (ctx)
86     av_zeroconf_shutdown(ctx);
87 #elif defined (USE_HOWL)
88   ho_zeroconf_unregister();
89 #elif defined (USE_BONJOUR)
90   bo_zeroconf_unregister();
91 #endif
92 }
93 #endif
94 
WZD_MODULE_INIT(void)95 int WZD_MODULE_INIT(void)
96 {
97   wzd_string_t * str;
98   int err;
99   int ret = 1;
100   void * arg = NULL;
101   const char *zeroconf_name = NULL;
102   const char *zeroconf_username = NULL;
103   const char *zeroconf_password = NULL;
104   const char *zeroconf_path = NULL;
105   unsigned long wzdftpd_port;
106 
107   if (initialized > 0) return 1;
108   initialized++;
109 
110   /* the mDNS name that should be published */
111   str = config_get_string(mainConfig->cfg_file, "ZEROCONF", "zeroconf_name", NULL);
112   if (str) {
113     zeroconf_name = strdup(str_tochar(str));
114     str_deallocate(str);
115   }
116 
117   /* TXT keys - see http://www.dns-sd.org/ServiceTypes.html */
118   str = config_get_string(mainConfig->cfg_file, "ZEROCONF", "zeroconf_username", NULL);
119   if (str) {
120     zeroconf_username = strdup(str_tochar(str));
121     str_deallocate(str);
122   }
123 
124   str = config_get_string(mainConfig->cfg_file, "ZEROCONF", "zeroconf_password", NULL);
125   if (str) {
126     zeroconf_password = strdup(str_tochar(str));
127     str_deallocate(str);
128   }
129 
130   str = config_get_string(mainConfig->cfg_file, "ZEROCONF", "zeroconf_path", NULL);
131   if (str) {
132     zeroconf_path = strdup(str_tochar(str));
133     str_deallocate(str);
134   }
135 
136   /** the actual port
137    * \todo determine port(s) dynamically from port = ...
138    *  \todo support multiple ports
139    */
140   wzdftpd_port = config_get_integer(mainConfig->cfg_file, "ZEROCONF", "zeroconf_port", &err);
141   if (err) {
142     out_log(LEVEL_CRITICAL,"zeroconf: you must provide zeroconf_port=... in your config file\n");
143     initialized = 0;
144     return -1;
145   }
146 
147 #ifdef ZEROCONF_USE_PROCESS
148   pid_child = fork();
149   if (pid_child < 0) {
150     out_log(LEVEL_CRITICAL,"zeroconf: could not create a new process\n");
151     initialized = 0;
152     return -1;
153   }
154   if (pid_child > 0) {
155     return 0;
156   }
157   {
158     sigset_t mask;
159     sigfillset(&mask);
160     ret = pthread_sigmask(SIG_UNBLOCK,&mask,NULL);
161     if (pid_child < 0) {
162       out_log(LEVEL_CRITICAL,"zeroconf: could not unblock pthread signals mask\n");
163       initialized = 0;
164       return -1;
165     }
166   }
167 # ifdef DEBUG
168   /* We MUST close stdin, or the keyboard interrupts (like Ctrl+C) will be sent to both
169    * the server and the zeroconf module
170    */
171   close(0);
172 # endif
173   /* We have to override ALL the signals trapped in wzd_ServerThread.c
174    * Since this process is created by fork(), it inheritates the file descriptors AND signal
175    * handlers, so the server's handler would be called in the forked process !
176    */
177   signal(SIGINT,sighandler);
178   signal(SIGTERM,sighandler);
179   signal(SIGHUP,SIG_DFL);
180 #endif
181 
182 #ifdef USE_BONJOUR
183   /*
184     TODO: This has to be tested on a OSX box. Especially whether it
185     blocks the main wzdftpd loop/thread.
186   */
187   bo_zeroconf_setup(wzdftpd_port,
188                     zeroconf_name,
189                     zeroconf_username,
190                     zeroconf_password,
191                     zeroconf_path);
192 #elif defined (USE_AVAHI)
193   if (wzdftpd_port == 0) return 1; // the port should be defined in the config file
194 
195   ctx = av_zeroconf_setup(wzdftpd_port,
196                           zeroconf_name,
197                           zeroconf_username,
198                           zeroconf_password,
199                           zeroconf_path);
200 #elif defined (USE_HOWL)
201   ho_zeroconf_setup(wzdftpd_port,
202                     zeroconf_name,
203                     zeroconf_username,
204                     zeroconf_password,
205                     zeroconf_path);
206 #endif
207 
208   out_log(LEVEL_INFO, "Module zeroconf loaded\n");
209 
210 #ifdef ZEROCONF_USE_PROCESS
211   routine(arg);
212   exit (0);
213 #else
214   ret = wzd_thread_create (&zeroconf_thread, NULL, &routine, arg);
215 #endif
216 
217   return 0;
218 }
219 
WZD_MODULE_CLOSE(void)220 void WZD_MODULE_CLOSE(void)
221 {
222   int ret;
223 #ifndef ZEROCONF_USE_PROCESS
224   void * thread_return;
225 #endif
226 
227   if (initialized) {
228 #ifdef ZEROCONF_USE_PROCESS
229     kill(pid_child,SIGTERM);
230 #else
231 # ifdef USE_AVAHI
232     if (ctx)
233       av_zeroconf_shutdown(ctx);
234 # elif defined (USE_BONJOUR)
235     bo_zeroconf_unregister();
236 # elif defined (USE_HOWL)
237     ho_zeroconf_unregister();
238 # endif
239 #endif
240 
241 #ifdef ZEROCONF_USE_PROCESS
242     ret = wait4(pid_child, NULL, 0, NULL);
243 #else
244     ret = wzd_thread_join(&zeroconf_thread, &thread_return);
245 #endif
246   }
247 
248   out_log(LEVEL_INFO, "Module zeroconf unloaded\n");
249 }
250 
routine(void * arg)251 static void * routine (void * arg)
252 {
253 #ifdef USE_AVAHI
254   /* Now start the loop.
255    * Note: run does not block the main loop. Tho It will create a new thread.
256    */
257   av_zeroconf_run(ctx);
258 # ifndef ZEROCONF_USE_PROCESS
259 # endif
260 #elif defined (USE_HOWL)
261   ho_zeroconf_run();
262 #elif defined (USE_BONJOUR)
263   bo_zeroconf_run();
264 #endif
265 
266   return NULL;
267 }
268