]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/afp_mdns.c
Fix SIGHUP config reloading
[netatalk.git] / etc / afpd / afp_mdns.c
1 /*
2  * Author:   Lee Essen <lee.essen@nowonline.co.uk>
3  * Based on: avahi support from Daniel S. Haischt <me@daniel.stefan.haischt.name>
4  * Purpose:  mdns based Zeroconf support
5  *
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12 #ifdef HAVE_MDNS
13
14 #include <unistd.h>
15 #include <time.h>
16 #include <pthread.h>
17 #include <poll.h>
18
19 #include <atalk/logger.h>
20 #include <atalk/util.h>
21 #include <atalk/dsi.h>
22 #include <atalk/unicode.h>
23 #include <atalk/netatalk_conf.h>
24
25 #include "afp_zeroconf.h"
26 #include "afp_mdns.h"
27
28 /*
29  * We'll store all the DNSServiceRef's here so that we can
30  * deallocate them later
31  */
32 static DNSServiceRef   *svc_refs = NULL;
33 static int             svc_ref_count = 0;
34 static pthread_t       poller;
35
36 /*
37  * Its easier to use asprintf to set the TXT record values
38  */
39 #define TXTRecordPrintf(rec, key, args...) {            \
40         char *str;                                      \
41         asprintf(&str, args);                           \
42         TXTRecordSetValue(rec, key, strlen(str), str);  \
43         free(str);                                      \
44     }
45 #define TXTRecordKeyPrintf(rec, k, var, args...) {      \
46         char *key, *str;                                \
47         asprintf(&key, k, var);                         \
48         asprintf(&str, args);                           \
49         TXTRecordSetValue(rec, key, strlen(str), str);  \
50         free(str); free(key);                           \
51     }
52
53 static struct pollfd *fds;
54
55 /*
56  * This is the thread that polls the filehandles
57  */
58 static void *polling_thread(void *arg) {
59     // First we loop through getting the filehandles and adding them to our poll, we
60     // need to allocate our pollfd's
61     DNSServiceErrorType error;
62     fds = calloc(svc_ref_count, sizeof(struct pollfd));
63     assert(fds);
64
65     for(int i=0; i < svc_ref_count; i++) {
66         int fd = DNSServiceRefSockFD(svc_refs[i]);
67         fds[i].fd = fd;
68         fds[i].events = POLLIN;
69     }
70
71     // Now we can poll and process the results...
72     while(poll(fds, svc_ref_count, -1) > 0) {
73         for(int i=0; i < svc_ref_count; i++) {
74             if(fds[i].revents & POLLIN) {
75                 error = DNSServiceProcessResult(svc_refs[i]);
76             }
77         }
78     }
79     return(NULL);
80 }
81
82 /*
83  * This is the callback for the service register function ... actually there isn't a lot
84  * we can do if we get problems, so we don't really need to do anything other than report
85  * the issue.
86  */
87 static void RegisterReply(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode,
88                           const char *name, const char *regtype, const char *domain, void *context)
89 {
90     if (errorCode != kDNSServiceErr_NoError) {
91         LOG(log_error, logtype_afpd, "Failed to register mDNS service: %s%s%s: code=%d",
92             name, regtype, domain, errorCode);
93     }
94 }
95
96 /*
97  * This function unregisters anything we have already
98  * registered and frees associated memory
99  */
100 static void unregister_stuff() {
101     pthread_cancel(poller);    
102
103     for (int i = 0; i < svc_ref_count; i++)
104         close(fds[i].fd);
105     free(fds);
106     fds = NULL;
107
108     if(svc_refs) {
109         for(int i=0; i < svc_ref_count; i++) {
110             DNSServiceRefDeallocate(svc_refs[i]);
111         }
112         free(svc_refs);
113         svc_refs = NULL;
114         svc_ref_count = 0;
115     }
116 }
117
118 /*
119  * This function tries to register the AFP DNS
120  * SRV service type.
121  */
122 static void register_stuff(const AFPObj *obj) {
123     uint                                        port;
124     const struct vol                *volume;
125     DSI                                         *dsi;
126     char                                        name[MAXINSTANCENAMELEN+1];
127     DNSServiceErrorType         error;
128     TXTRecordRef                        txt_adisk;
129     TXTRecordRef                        txt_devinfo;
130     char                                        tmpname[256];
131
132     // If we had already registered, then we will unregister and re-register
133     if(svc_refs) unregister_stuff();
134
135     /* Register our service, prepare the TXT record */
136     TXTRecordCreate(&txt_adisk, 0, NULL);
137     TXTRecordPrintf(&txt_adisk, "sys", "waMa=0,adVF=0x100");
138
139     /* Build AFP volumes list */
140     int i = 0;
141
142     for (volume = getvolumes(); volume; volume = volume->v_next) {
143
144         if (convert_string(CH_UCS2, CH_UTF8_MAC, volume->v_u8mname, -1, tmpname, 255) <= 0) {
145             LOG ( log_error, logtype_afpd, "Could not set Zeroconf volume name for TimeMachine");
146             goto fail;
147         }
148
149         if (volume->v_flags & AFPVOL_TM) {
150             if (volume->v_uuid) {
151                 LOG(log_info, logtype_afpd, "Registering volume '%s' with UUID: '%s' for TimeMachine",
152                     volume->v_localname, volume->v_uuid);
153                 TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1,adVU=%s",
154                                    tmpname, volume->v_uuid);
155             } else {
156                 LOG(log_warning, logtype_afpd, "Registering volume '%s' for TimeMachine. But UUID is invalid.",
157                     volume->v_localname);
158                 TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1", tmpname);
159             }
160         }
161     }
162
163     // Now we can count the configs so we know how many service
164     // records to allocate
165     for (dsi = obj->dsi; dsi; dsi = dsi->next) {
166         svc_ref_count++;                    // AFP_DNS_SERVICE_TYPE
167         if (i) svc_ref_count++;      // ADISK_SERVICE_TYPE
168         if (obj->options.mimicmodel) svc_ref_count++;        // DEV_INFO_SERVICE_TYPE
169     }
170
171     // Allocate the memory to store our service refs
172     svc_refs = calloc(svc_ref_count, sizeof(DNSServiceRef));
173     assert(svc_ref);
174     svc_ref_count = 0;
175
176     /* AFP server */
177     for (dsi = obj->dsi; dsi; dsi = dsi->next) {
178
179         port = getip_port((struct sockaddr *)&dsi->server);
180
181         if (convert_string(obj->options.unixcharset,
182                            CH_UTF8,
183                            obj->options.hostname,
184                            -1,
185                            name,
186                            MAXINSTANCENAMELEN) <= 0) {
187             LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name");
188             goto fail;
189         }
190         if ((dsi->bonjourname = strdup(name)) == NULL) {
191             LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name");
192             goto fail;
193
194         }
195         LOG(log_info, logtype_afpd, "Registering server '%s' with Bonjour",
196             dsi->bonjourname);
197
198         error = DNSServiceRegister(&svc_refs[svc_ref_count++],
199                                    0,               // no flags
200                                    0,               // all network interfaces
201                                    dsi->bonjourname,
202                                    AFP_DNS_SERVICE_TYPE,
203                                    "",            // default domains
204                                    NULL,            // default host name
205                                    htons(port),
206                                    0,               // length of TXT
207                                    NULL,            // no TXT
208                                    RegisterReply,           // callback
209                                    NULL);       // no context
210         if(error != kDNSServiceErr_NoError) {
211             LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
212                 AFP_DNS_SERVICE_TYPE, error);
213             goto fail;
214         }
215
216         if(i) {
217             error = DNSServiceRegister(&svc_refs[svc_ref_count++],
218                                        0,               // no flags
219                                        0,               // all network interfaces
220                                        dsi->bonjourname,
221                                        ADISK_SERVICE_TYPE,
222                                        "",            // default domains
223                                        NULL,            // default host name
224                                        htons(port),
225                                        TXTRecordGetLength(&txt_adisk),
226                                        TXTRecordGetBytesPtr(&txt_adisk),
227                                        RegisterReply,           // callback
228                                        NULL);       // no context
229             if(error != kDNSServiceErr_NoError) {
230                 LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
231                     ADISK_SERVICE_TYPE, error);
232                 goto fail;
233             }
234         }
235
236         if (obj->options.mimicmodel) {
237             LOG(log_info, logtype_afpd, "Registering server '%s' with model '%s'",
238                 dsi->bonjourname, obj->options.mimicmodel);
239             TXTRecordCreate(&txt_devinfo, 0, NULL);
240             TXTRecordPrintf(&txt_devinfo, "model", obj->options.mimicmodel);
241             error = DNSServiceRegister(&svc_refs[svc_ref_count++],
242                                        0,               // no flags
243                                        0,               // all network interfaces
244                                        dsi->bonjourname,
245                                        DEV_INFO_SERVICE_TYPE,
246                                        "",            // default domains
247                                        NULL,            // default host name
248                                        /*
249                                         * We would probably use port 0 zero, but we can't, from man DNSServiceRegister:
250                                         *   "A value of 0 for a port is passed to register placeholder services.
251                                         *    Place holder services are not found  when browsing, but other
252                                         *    clients cannot register with the same name as the placeholder service."
253                                         * We therefor use port 9 which is used by the adisk service type.
254                                         */
255                                        htons(9),
256                                        TXTRecordGetLength(&txt_devinfo),
257                                        TXTRecordGetBytesPtr(&txt_devinfo),
258                                        RegisterReply,           // callback
259                                        NULL);       // no context
260             TXTRecordDeallocate(&txt_devinfo);
261             if(error != kDNSServiceErr_NoError) {
262                 LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
263                     DEV_INFO_SERVICE_TYPE, error);
264                 goto fail;
265             }
266         } /* if (config->obj.options.mimicmodel) */
267     }   /* for config*/
268
269         /*
270          * Now we can create the thread that will poll for the results
271          * and handle the calling of the callbacks
272          */
273     if(pthread_create(&poller, NULL, polling_thread, NULL) != 0) {
274         LOG(log_error, logtype_afpd, "Unable to start mDNS polling thread");
275         goto fail;
276     }
277
278 fail:
279     TXTRecordDeallocate(&txt_adisk);
280     return;
281 }
282
283 /************************************************************************
284  * Public funcions
285  ************************************************************************/
286
287 /*
288  * Tries to setup the Zeroconf thread and any
289  * neccessary config setting.
290  */
291 void md_zeroconf_register(const AFPObj *obj) {
292     int error;
293
294     register_stuff(obj);
295     return;
296 }
297
298 /*
299  * Tries to shutdown this loop impl.
300  * Call this function from inside this thread.
301  */
302 int md_zeroconf_unregister() {
303     unregister_stuff();
304     return 0;
305 }
306
307 #endif /* USE_MDNS */
308