"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