"Fossies" - the Fresh Open Source Software Archive 
Member "fusesmb-0.8.7/cache.c" (4 May 2006, 14762 Bytes) of package /linux/privat/old/fusesmb-0.8.7.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "cache.c" see the
Fossies "Dox" file reference documentation.
1 /*
2 * Copyright (C) 2006 Vincent Wagelaar
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 #include <libsmbclient.h>
24 #include <pthread.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <ctype.h>
29 #include <sys/param.h>
30 #include <stdlib.h>
31 #include <errno.h>
32 #include <sys/types.h>
33 #include <unistd.h>
34
35 #include "stringlist.h"
36 #include "smbctx.h"
37 #include "hash.h"
38 #include "configfile.h"
39 #include "debug.h"
40
41 #define MAX_SERVERLEN 255
42 #define MAX_WGLEN 255
43
44
45 stringlist_t *cache;
46 pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
47
48 struct fusesmb_cache_opt {
49 stringlist_t *ignore_servers;
50 stringlist_t *ignore_workgroups;
51 };
52
53
54 config_t cfg;
55 struct fusesmb_cache_opt opts;
56
57 static void options_read(config_t *cfg, struct fusesmb_cache_opt *opt)
58 {
59 opt->ignore_servers = NULL;
60 if (-1 == config_read_stringlist(cfg, "ignore", "servers", &(opt->ignore_servers), ','))
61 {
62 opt->ignore_servers = NULL;
63 }
64 opt->ignore_workgroups = NULL;
65 if (-1 == config_read_stringlist(cfg, "ignore", "workgroups", &(opt->ignore_workgroups), ','))
66 {
67 opt->ignore_workgroups = NULL;
68 }
69 }
70
71 static void options_free(struct fusesmb_cache_opt *opt)
72 {
73 if (NULL != opt->ignore_servers)
74 {
75 sl_free(opt->ignore_servers);
76 }
77 if (NULL != opt->ignore_workgroups)
78 {
79 sl_free(opt->ignore_workgroups);
80 }
81 }
82
83
84 /*
85 * Some servers refuse to return a server list using libsmbclient, so using
86 * broadcast lookup through nmblookup
87 */
88 static int nmblookup(const char *wg, stringlist_t *sl, hash_t *ipcache)
89 {
90 /* Find all ips for the workgroup by running :
91 $ nmblookup 'workgroup_name'
92 */
93 char wg_cmd[512];
94 snprintf(wg_cmd, 512, "nmblookup '%s'", wg);
95 //fprintf(stderr, "%s\n", cmd);
96 FILE *pipe;
97 pipe = popen(wg_cmd, "r");
98 if (pipe == NULL)
99 return -1;
100
101 int ip_cmd_size = 8192;
102 char *ip_cmd = (char *)malloc(ip_cmd_size * sizeof(char));
103 if (ip_cmd == NULL)
104 return -1;
105 strcpy(ip_cmd, "nmblookup -A ");
106 int ip_cmd_len = strlen(ip_cmd);
107 while (!feof(pipe))
108 {
109 /* Parse output that looks like this:
110 querying boerderie on 172.20.91.255
111 172.20.89.134 boerderie<00>
112 172.20.89.191 boerderie<00>
113 172.20.88.213 boerderie<00>
114 */
115 char buf[4096];
116 if (NULL == fgets(buf, 4096, pipe))
117 continue;
118
119 char *pip = buf;
120 /* Yes also include the space */
121 while (isdigit(*pip) || *pip == '.' || *pip == ' ')
122 {
123 pip++;
124 }
125 *pip = '\0';
126 int len = strlen(buf);
127 if (len == 0) continue;
128 ip_cmd_len += len;
129 if (ip_cmd_len >= (ip_cmd_size -1))
130 {
131 ip_cmd_size *= 2;
132 char *tmp = realloc(ip_cmd, ip_cmd_size *sizeof(char));
133 if (tmp == NULL)
134 {
135 ip_cmd_size /= 2;
136 ip_cmd_len -= len;
137 continue;
138 }
139 ip_cmd = tmp;
140 }
141 /* Append the ip to the command:
142 $ nmblookup -A ip1 ... ipn
143 */
144 strcat(ip_cmd, buf);
145 }
146 pclose(pipe);
147
148 if (strlen(ip_cmd) == 13)
149 {
150 free(ip_cmd);
151 return 0;
152 }
153 debug("%s\n", ip_cmd);
154 pipe = popen(ip_cmd, "r");
155 if (pipe == NULL)
156 {
157 free(ip_cmd);
158 return -1;
159 }
160
161 while (!feof(pipe))
162 {
163 char buf2[4096];
164 char buf[4096];
165 char ip[32];
166
167 char *start = buf;
168 if (NULL == fgets(buf2, 4096, pipe))
169 continue;
170 /* Parse following input:
171 Looking up status of 123.123.123.123
172 SERVER <00> - B <ACTIVE>
173 SERVER <03> - B <ACTIVE>
174 SERVER <20> - B <ACTIVE>
175 ..__MSBROWSE__. <01> - <GROUP> B <ACTIVE>
176 WORKGROUP <00> - <GROUP> B <ACTIVE>
177 WORKGROUP <1d> - B <ACTIVE>
178 WORKGROUP <1e> - <GROUP> B <ACTIVE>
179 */
180 if (strncmp(buf2, "Looking up status of ", strlen("Looking up status of ")) == 0)
181 {
182 char *tmp = rindex(buf2, ' ');
183 tmp++;
184 char *end = index(tmp, '\n');
185 *end = '\0';
186 strcpy(ip, tmp);
187 debug("%s", ip);
188 }
189 else
190 {
191 continue;
192 }
193
194 while (!feof(pipe))
195 {
196
197 if (NULL == fgets(buf, 4096, pipe))
198 break;
199 char *sep = buf;
200
201 if (*buf != '\t')
202 break;
203 if (NULL != strstr(buf, "<GROUP>"))
204 break;
205 if (NULL == (sep = strstr(buf, "<00>")))
206 break;
207 *sep = '\0';
208
209 start++;
210
211 while (*sep == '\t' || *sep == ' ' || *sep == '\0')
212 {
213 *sep = '\0';
214 sep--;
215 }
216 sl_add(sl, start, 1);
217 if (NULL == hash_lookup(ipcache, start))
218 hash_alloc_insert(ipcache, strdup(start), strdup(ip));
219 debug("%s : %s", ip, start);
220 }
221
222 }
223 pclose(pipe);
224 free(ip_cmd);
225 return 0;
226 }
227
228 static int server_listing(SMBCCTX *ctx, stringlist_t *cache, const char *wg, const char *sv, const char *ip)
229 {
230 //return 0;
231 char tmp_path[MAXPATHLEN] = "smb://";
232 if (ip != NULL)
233 {
234 strcat(tmp_path, ip);
235 }
236 else
237 {
238 strcat(tmp_path, sv);
239 }
240
241 struct smbc_dirent *share_dirent;
242 SMBCFILE *dir;
243 //SMBCCTX *ctx = fusesmb_new_context();
244 dir = ctx->opendir(ctx, tmp_path);
245 if (dir == NULL)
246 {
247 //smbc_free_context(ctx, 1);
248 ctx->closedir(ctx, dir);
249 return -1;
250 }
251
252 while (NULL != (share_dirent = ctx->readdir(ctx, dir)))
253 {
254 if (//share_dirent->name[strlen(share_dirent->name)-1] == '$' ||
255 share_dirent->smbc_type != SMBC_FILE_SHARE ||
256 share_dirent->namelen == 0)
257 continue;
258 if (0 == strcmp("ADMIN$", share_dirent->name) ||
259 0 == strcmp("print$", share_dirent->name))
260 continue;
261 int len = strlen(wg)+ strlen(sv) + strlen(share_dirent->name) + 4;
262 char tmp[len];
263 snprintf(tmp, len, "/%s/%s/%s", wg, sv, share_dirent->name);
264 debug("%s", tmp);
265 pthread_mutex_lock(&cache_mutex);
266 if (-1 == sl_add(cache, tmp, 1))
267 {
268 pthread_mutex_unlock(&cache_mutex);
269 fprintf(stderr, "sl_add failed\n");
270 ctx->closedir(ctx, dir);
271 //smbc_free_context(ctx, 1);
272 return -1;
273 }
274 pthread_mutex_unlock(&cache_mutex);
275
276 }
277 ctx->closedir(ctx, dir);
278 //smbc_free_context(ctx, 1);
279 return 0;
280 }
281
282 static void *workgroup_listing_thread(void *args)
283 {
284 char *wg = (char *)args;
285 //SMBCCTX *ctx, stringlist_t *cache, hash_t *ip_cache, const char *wg
286
287 hash_t *ip_cache = hash_create(HASHCOUNT_T_MAX, NULL, NULL);
288 if (NULL == ip_cache)
289 return NULL;
290
291 stringlist_t *servers = sl_init();
292 if (NULL == servers)
293 {
294 fprintf(stderr, "Malloc failed\n");
295 return NULL;
296 }
297 SMBCCTX *ctx = fusesmb_cache_new_context(&cfg);
298 SMBCFILE *dir;
299 char temp_path[MAXPATHLEN] = "smb://";
300 strcat(temp_path, wg);
301 debug("Looking up Workgroup: %s", wg);
302 struct smbc_dirent *server_dirent;
303 dir = ctx->opendir(ctx, temp_path);
304 if (dir == NULL)
305 {
306 ctx->closedir(ctx, dir);
307
308 goto use_popen;
309 }
310 while (NULL != (server_dirent = ctx->readdir(ctx, dir)))
311 {
312 if (server_dirent->namelen == 0 ||
313 server_dirent->smbc_type != SMBC_SERVER)
314 {
315 continue;
316 }
317
318 if (-1 == sl_add(servers, server_dirent->name, 1))
319 continue;
320
321
322 }
323 ctx->closedir(ctx, dir);
324
325 use_popen:
326
327
328 nmblookup(wg, servers, ip_cache);
329 sl_casesort(servers);
330
331 size_t i;
332 for (i=0; i < sl_count(servers); i++)
333 {
334 /* Skip duplicates */
335 if (i > 0 && strcmp(sl_item(servers, i), sl_item(servers, i-1)) == 0)
336 continue;
337
338 /* Check if this server is in the ignore list in fusesmb.conf */
339 if (NULL != opts.ignore_servers)
340 {
341 if (NULL != sl_find(opts.ignore_servers, sl_item(servers, i)))
342 {
343 debug("Ignoring %s", sl_item(servers, i));
344 continue;
345 }
346 }
347 char sv[1024] = "/";
348 strcat(sv, sl_item(servers, i));
349 int ignore = 0;
350
351 /* Check if server specific option says ignore */
352 if (0 == config_read_bool(&cfg, sv, "ignore", &ignore))
353 {
354 if (ignore == 1)
355 continue;
356 }
357
358 hnode_t *node = hash_lookup(ip_cache, sl_item(servers, i));
359 if (node == NULL)
360 server_listing(ctx, cache, wg, sl_item(servers, i), NULL);
361 else
362 server_listing(ctx, cache, wg, sl_item(servers, i), hnode_get(node));
363 }
364
365 hscan_t sc;
366 hnode_t *n;
367 hash_scan_begin(&sc, ip_cache);
368 while (NULL != (n = hash_scan_next(&sc)))
369 {
370 void *data = hnode_get(n);
371 const void *key = hnode_getkey(n);
372 hash_scan_delfree(ip_cache, n);
373 free((void *)key);
374 free(data);
375
376 }
377 hash_destroy(ip_cache);
378 sl_free(servers);
379 smbc_free_context(ctx, 1);
380 return 0;
381 }
382
383
384 int cache_servers(SMBCCTX *ctx)
385 {
386 //SMBCCTX *ctx = fusesmb_new_context();
387 SMBCFILE *dir;
388 struct smbc_dirent *workgroup_dirent;
389
390 /* Initialize cache */
391 cache = sl_init();
392 size_t i;
393
394
395 dir = ctx->opendir(ctx, "smb://");
396
397 if (dir == NULL)
398 {
399 ctx->closedir(ctx, dir);
400 sl_free(cache);
401 //smbc_free_context(ctx, 1);
402 return -1;
403 }
404
405 pthread_t *threads;
406 threads = (pthread_t *)malloc(sizeof(pthread_t));
407 if (NULL == threads)
408 return -1;
409 pthread_attr_t thread_attr;
410 pthread_attr_init(&thread_attr);
411 pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE);
412
413 unsigned int num_threads = 0;
414
415 while (NULL != (workgroup_dirent = ctx->readdir(ctx, dir)) )
416 {
417 if (workgroup_dirent->namelen == 0 ||
418 workgroup_dirent->smbc_type != SMBC_WORKGROUP)
419 {
420 continue;
421 }
422 //char wg[1024];
423 //strncpy(wg, workgroup_dirent->name, 1024);
424 char *thread_arg = strdup(workgroup_dirent->name);
425
426 if (opts.ignore_workgroups != NULL)
427 {
428 if (NULL != sl_find(opts.ignore_workgroups, workgroup_dirent->name))
429 {
430 debug("Ignoring Workgroup: %s", workgroup_dirent->name);
431 continue;
432 }
433 }
434
435 if (NULL == thread_arg)
436 continue;
437 int rc;
438 rc = pthread_create(&threads[num_threads],
439 &thread_attr, workgroup_listing_thread,
440 (void*)thread_arg);
441 //workgroup_listing(ctx, cache, ip_cache, wg);
442 if (rc)
443 {
444 fprintf(stderr, "Failed to create thread for workgroup: %s\n", workgroup_dirent->name);
445 free(thread_arg);
446 continue;
447 }
448 num_threads++;
449 threads = (pthread_t *)realloc(threads, (num_threads+1)*sizeof(pthread_t));
450 }
451 ctx->closedir(ctx, dir);
452
453 //smbc_free_context(ctx, 1);
454
455 pthread_attr_destroy(&thread_attr);
456
457 for (i=0; i<num_threads; i++)
458 {
459 int rc = pthread_join(threads[i], NULL);
460 if (rc)
461 {
462 fprintf(stderr, "Error while joining thread, errorcode: %d\n", rc);
463 exit(-1);
464 }
465 }
466 free(threads);
467
468 sl_casesort(cache);
469 char cachefile[1024];
470 char tmp_cachefile[1024];
471 snprintf(tmp_cachefile, 1024, "%s/.smb/fusesmb.cache.XXXXX", getenv("HOME"));
472 mkstemp(tmp_cachefile);
473 snprintf(cachefile, 1024, "%s/.smb/fusesmb.cache", getenv("HOME"));
474 mode_t oldmask;
475 oldmask = umask(022);
476 FILE *fp = fopen(tmp_cachefile, "w");
477 umask(oldmask);
478 if (fp == NULL)
479 {
480 sl_free(cache);
481 return -1;
482 }
483
484 for (i=0 ; i < sl_count(cache); i++)
485 {
486 fprintf(fp, "%s\n", sl_item(cache, i));
487 }
488 fclose(fp);
489 /* Make refreshing cache file atomic */
490 rename(tmp_cachefile, cachefile);
491 sl_free(cache);
492 return 0;
493 }
494
495 int main(int argc, char *argv[])
496 {
497 char pidfile[1024];
498 snprintf(pidfile, 1024, "%s/.smb/fusesmb.cache.pid", getenv("HOME"));
499
500 char configfile[1024];
501 snprintf(configfile, 1024, "%s/.smb/fusesmb.conf", getenv("HOME"));
502 if (-1 == config_init(&cfg, configfile))
503 {
504 fprintf(stderr, "Could not open config file: %s (%s)", configfile, strerror(errno));
505 exit(EXIT_FAILURE);
506 }
507 options_read(&cfg, &opts);
508
509 struct stat st;
510 if (argc == 1)
511 {
512 pid_t pid, sid;
513
514 if (-1 != stat(pidfile, &st))
515 {
516 if (time(NULL) - st.st_mtime > 30*60)
517 unlink(pidfile);
518 else
519 {
520 fprintf(stderr, "Error: %s is already running\n", argv[0]);
521 exit(EXIT_FAILURE);
522 }
523 }
524
525 pid = fork();
526 if (pid < 0)
527 exit(EXIT_FAILURE);
528 if (pid > 0)
529 exit(EXIT_SUCCESS);
530
531 sid = setsid();
532 if (sid < 0) {
533 exit(EXIT_FAILURE);
534 }
535 if (chdir("/") < 0)
536 exit(EXIT_FAILURE);
537
538 mode_t oldmask;
539 oldmask = umask(077);
540 FILE *fp = fopen(pidfile, "w");
541 umask(oldmask);
542 if (NULL == fp)
543 exit(EXIT_FAILURE);
544 fprintf(fp, "%i\n", sid);
545 fclose(fp);
546
547 close(STDIN_FILENO);
548 close(STDOUT_FILENO);
549 close(STDERR_FILENO);
550 }
551 SMBCCTX *ctx = fusesmb_cache_new_context(&cfg);
552 cache_servers(ctx);
553 smbc_free_context(ctx, 1);
554 options_free(&opts);
555 if (argc == 1)
556 {
557 unlink(pidfile);
558 }
559 exit(EXIT_SUCCESS);
560 }
561