1 /* vi:ai:et:ts=8 sw=2
2  */
3 /*
4  * wzdftpd - a modular and cool ftp server
5  * Copyright (C) 2002-2004  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 "wzd_all.h"
28 
29 #ifndef WZD_USE_PCH
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 
35 #include <time.h>
36 
37 #ifdef WIN32
38 #include <winsock2.h>
39 #else
40 #include <unistd.h>
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>	/* struct in_addr (wzd_misc.h) */
45 #endif
46 
47 #include <sys/stat.h>
48 
49 #include "wzd_structs.h"
50 
51 #include "wzd_libmain.h"
52 #include "wzd_misc.h"
53 
54 #include "wzd_configfile.h"
55 #include "wzd_fs.h"
56 #include "wzd_group.h"
57 #include "wzd_vars.h"
58 #include "wzd_log.h"
59 #include "wzd_mutex.h"
60 #include "wzd_user.h"
61 
62 
63 #include "wzd_debug.h"
64 
65 #endif /* WZD_USE_PCH */
66 
67 
68 
69 static struct wzd_shm_vars_t * _shm_vars[32] = { NULL };
70 
71 
72 
vars_get(const char * varname,void * data,unsigned int datalength,wzd_config_t * config)73 int vars_get(const char *varname, void *data, unsigned int datalength, wzd_config_t * config)
74 {
75   if (!config) return 1;
76 
77   if (strcasecmp(varname,"bw")==0) {
78     snprintf(data,datalength,"%lu",get_bandwidth(NULL,NULL));
79     return 0;
80   }
81   if (strcmp(varname,"loglevel")==0) {
82     const char * str;
83 
84     str = config_get_value(config->cfg_file, "GLOBAL", "loglevel");
85     if (str) {
86       snprintf(data,datalength,"%s",str);
87       return 0;
88     }
89     snprintf(data,datalength,"%s",loglevel2str(config->loglevel));
90     return 0;
91   }
92   if (strcasecmp(varname,"max_dl")==0) {
93     snprintf(data,datalength,"%u",config->global_dl_limiter.maxspeed);
94     return 0;
95   }
96   if (strcasecmp(varname,"max_threads")==0) {
97     snprintf(data,datalength,"%d",config->max_threads);
98     return 0;
99   }
100   if (strcasecmp(varname,"max_ul")==0) {
101     snprintf(data,datalength,"%u",config->global_ul_limiter.maxspeed);
102     return 0;
103   }
104   if (strcasecmp(varname,"pasv_low")==0) {
105     snprintf(data,datalength,"%u",config->pasv_low_range);
106     return 0;
107   }
108   if (strcasecmp(varname,"pasv_high")==0) {
109     snprintf(data,datalength,"%u",config->pasv_high_range);
110     return 0;
111   }
112   if (strcmp(varname,"port")==0) {
113     const char * str;
114 
115     str = config_get_value(config->cfg_file, "GLOBAL", "port");
116     if (str) {
117       snprintf(data,datalength,"%s",str);
118       return 0;
119     }
120     snprintf(data,datalength,"%u",config->port);
121     return 0;
122   }
123   if (strcmp(varname,"uptime")==0) {
124     time_t t;
125 
126     (void)time(&t);
127     t = t - config->server_start;
128     snprintf(data,datalength,"%lu",(unsigned long)t);
129     return 0;
130   }
131 
132   return 1;
133 }
134 
135 /** \brief Change value of server variable
136  *
137  * \todo we should change the value in config->cfg_file
138  */
vars_set(const char * varname,const void * data,unsigned int datalength,wzd_config_t * config)139 int vars_set(const char *varname, const void *data, unsigned int datalength, wzd_config_t * config)
140 {
141   int i;
142   unsigned long ul;
143   char *ptr;
144 
145   if (!data || !config) return 1;
146 
147   if (strcasecmp(varname,"deny_access_files_uploaded")==0) {
148     ul = strtoul(data,NULL,0);
149     if (ul==1) { CFG_SET_OPTION(config,CFG_OPT_DENY_ACCESS_FILES_UPLOADED); return 0; }
150     if (ul==0) { CFG_CLR_OPTION(config,CFG_OPT_DENY_ACCESS_FILES_UPLOADED); return 0; }
151     return 1;
152   }
153   if (strcasecmp(varname,"hide_dotted_files")==0) {
154     ul = strtoul(data,NULL,0);
155     if (ul==1) { CFG_SET_OPTION(config,CFG_OPT_HIDE_DOTTED_FILES); return 0; }
156     if (ul==0) { CFG_CLR_OPTION(config,CFG_OPT_HIDE_DOTTED_FILES); return 0; }
157     return 1;
158   }
159   if (strcasecmp(varname,"loglevel")==0) {
160     i = str2loglevel(data);
161     if (i==-1) {
162       return 1;
163     }
164     config->loglevel = i;
165     return 0;
166   }
167   if (strcasecmp(varname,"max_dl")==0) {
168     ul = strtoul(data,&ptr,0);
169     if (ptr && *ptr == '\0') {
170       config->global_dl_limiter.maxspeed = ul;
171       return 0;
172     }
173   }
174   if (strcasecmp(varname,"max_threads")==0) {
175     ul = strtoul(data,&ptr,0);
176     if (ptr && *ptr == '\0') {
177       config->max_threads = ul;
178       return 0;
179     }
180   }
181   if (strcasecmp(varname,"max_ul")==0) {
182     ul = strtoul(data,&ptr,0);
183     if (ptr && *ptr == '\0') {
184       config->global_ul_limiter.maxspeed = ul;
185       return 0;
186     }
187   }
188   if (strcasecmp(varname,"pasv_low")==0) {
189     ul = strtoul(data,NULL,0);
190     if (ul < 65535 && ul < config->pasv_high_range) {
191       config->pasv_low_range = ul;
192       return 0;
193     }
194   }
195   if (strcasecmp(varname,"pasv_high")==0) {
196     ul = strtoul(data,NULL,0);
197     if (ul < 65535 && ul > config->pasv_low_range) {
198       config->pasv_high_range = ul;
199       return 0;
200     }
201   }
202 
203   return 1;
204 }
205 
vars_user_get(const char * username,const char * varname,void * data,unsigned int datalength,wzd_config_t * config)206 int vars_user_get(const char *username, const char *varname, void *data, unsigned int datalength, wzd_config_t * config)
207 {
208   wzd_user_t * user;
209   wzd_group_t * group;
210 
211   if (!username || !varname) return 1;
212 
213   user = GetUserByName(username);
214   if (!user) return 1;
215 
216   if (strcasecmp(varname,"group")==0) {
217     if (user->group_num > 0) {
218       group = GetGroupByID(user->groups[0]);
219       snprintf(data,datalength,"%s",group->groupname);
220     } else
221       snprintf(data,datalength,"no group");
222     return 0;
223   }
224   if (strcasecmp(varname,"home")==0) {
225     snprintf(data,datalength,"%s",user->rootpath);
226     return 0;
227   }
228   if (strcasecmp(varname,"max_dl")==0) {
229     snprintf(data,datalength,"%u",user->max_dl_speed);
230     return 0;
231   }
232   if (strcasecmp(varname,"max_ul")==0) {
233     snprintf(data,datalength,"%u",user->max_ul_speed);
234     return 0;
235   }
236   if (strcasecmp(varname,"credits")==0) {
237     snprintf(data,datalength,"%" PRIu64,user->credits);
238     return 0;
239   }
240   if (strcasecmp(varname,"name")==0) {
241     snprintf(data,datalength,"%s",user->username);
242     return 0;
243   }
244   if (strcasecmp(varname,"tag")==0) {
245     if (user->tagline[0] != '\0')
246       snprintf(data,datalength,"%s",user->tagline);
247     else
248       snprintf(data,datalength,"no tagline set");
249     return 0;
250   }
251 
252   return 1;
253 }
254 
vars_user_addip(const char * username,const char * ip,wzd_config_t * config)255 int vars_user_addip(const char *username, const char *ip, wzd_config_t *config)
256 {
257   wzd_user_t *user;
258   int ret;
259 
260   if (!username || !ip) return 1;
261 
262   user = GetUserByName(username);
263   if (!user) return -1;
264 
265   do {
266 
267     ret = ip_inlist(user->ip_list, ip);
268     if (ret) return 1; /* already present */
269 
270     ret = ip_add_check(&user->ip_list, ip, 1 /* is_allowed */);
271 
272 /*    ip = strtok_r(NULL," \t\r\n",&ptr);*/
273     ip = NULL; /** \todo add only one ip (for the moment) */
274   } while (ip);
275 
276   /* commit to backend */
277   return backend_mod_user(config->backends->filename, user->uid, user, _USER_IP);
278 }
279 
vars_user_delip(const char * username,const char * ip,wzd_config_t * config)280 int vars_user_delip(const char *username, const char *ip, wzd_config_t *config)
281 {
282   char *ptr_ul;
283   wzd_user_t *user;
284   unsigned long ul;
285   int ret;
286 
287   if (!username || !ip) return 1;
288 
289   user = GetUserByName(username);
290   if (!user) return -1;
291 
292   do {
293 
294     /* try to take argument as a slot number */
295     ul = strtoul(ip,&ptr_ul,0);
296     if (*ptr_ul=='\0') {
297       unsigned int i;
298       struct wzd_ip_list_t * current_ip;
299 
300       current_ip = user->ip_list;
301       for (i=1; i<ul && current_ip != NULL; i++) {
302         current_ip = current_ip->next_ip;
303       }
304       if (current_ip == NULL) return 2; /* not found */
305       ret = ip_remove(&user->ip_list,current_ip->regexp);
306       if (ret != 0) return -1;
307     } else { /* if (*ptr=='\0') */
308 
309       ret = ip_remove(&user->ip_list,ip);
310       if (ret != 0) return 3;
311 
312     } /* if (*ptr=='\0') */
313 
314 /*    ip = strtok_r(NULL," \t\r\n",&ptr);*/
315     ip = NULL; /** \todo add only one ip (for the moment) */
316   } while (ip);
317 
318   /* commit to backend */
319   return backend_mod_user(config->backends->filename, user->uid, user, _USER_IP);
320 }
321 
vars_user_set(const char * username,const char * varname,const void * data,unsigned int datalength,wzd_config_t * config)322 int vars_user_set(const char *username, const char *varname, const void *data, unsigned int datalength, wzd_config_t * config)
323 {
324   wzd_user_t * user;
325   unsigned long mod_type;
326   unsigned long ul;
327   char *ptr;
328   int ret;
329 
330   if (!username || !varname) return 1;
331 
332   user = GetUserByName(username);
333   if (!user) return -1;
334 
335   /* find modification type */
336   mod_type = _USER_NOTHING;
337 
338   /* addip */
339   if (strcmp(varname, "addip")==0) {
340     return vars_user_addip(username, data, config);
341   }
342   /* credits */
343   else if (strcmp(varname, "credits")==0) {
344     u64_t ull;
345 
346     ull = strtoull(data, &ptr, 0); /** \todo XXX check overflows */
347 
348     user->credits = ull;
349     mod_type = _USER_CREDITS;
350   }
351   /* bytes_ul and bytes_dl should never be changed ... */
352   /* delip */
353   else if (strcmp(varname, "delip")==0) {
354     return vars_user_delip(username, data, config);
355   }
356   /* flags */ /* TODO accept modifications style +f or -f */
357   else if (strcmp(varname, "flags")==0) {
358     strncpy(user->flags, data, MAX_FLAGS_NUM-1);
359     mod_type = _USER_FLAGS;
360   }
361   /* homedir */
362   else if (strcmp(varname, "home")==0) {
363     /* check if homedir exist */
364     {
365       fs_filestat_t s;
366       if (fs_file_stat(data,&s) || !S_ISDIR(s.mode)) {
367         /* Homedir does not exist */
368         return 1;
369       }
370     }
371     mod_type = _USER_ROOTPATH;
372     strncpy(user->rootpath, data, WZD_MAX_PATH);
373   }
374   /* leech_slots */
375   else if (strcmp(varname, "leech_slots")==0) {
376     ul=strtoul(data, &ptr, 0);
377     /* TODO compare with USHORT_MAX */
378     if (*ptr) return -1;
379     mod_type = _USER_LEECHSLOTS; user->leech_slots = (unsigned short)ul;
380   }
381   /* max_dl */
382   else if (strcmp(varname, "max_dl")==0) {
383     ul=strtoul(data, &ptr, 0);
384     if (*ptr) return -1;
385     mod_type = _USER_MAX_DLS; user->max_dl_speed = ul;
386   }
387   /* max_idle */
388   else if (strcmp(varname, "max_idle")==0) {
389     ul=strtoul(data, &ptr, 0);
390     if (*ptr) return -1;
391     mod_type = _USER_IDLE; user->max_idle_time = ul;
392   }
393   /* max_ul */
394   else if (strcmp(varname, "max_ul")==0) {
395     ul=strtoul(data, &ptr, 0);
396     if (*ptr) return -1;
397     mod_type = _USER_MAX_ULS; user->max_ul_speed = ul;
398   }
399   /* num_logins */
400   else if (strcmp(varname, "num_logins")==0) {
401     ul=strtoul(data, &ptr,0);
402     if (*ptr) return -1;
403     mod_type = _USER_NUMLOGINS; user->num_logins = (unsigned short)ul;
404   }
405   /* pass */
406   else if (strcmp(varname, "pass")==0) {
407     mod_type = _USER_USERPASS;
408     strncpy(user->userpass, data, sizeof(user->userpass));
409   }
410   /* perms */
411   else if (strcmp(varname, "perms")==0) {
412     ul=strtoul(data, &ptr, 0);
413     if (*ptr) return -1;
414     mod_type = _USER_PERMS; user->userperms = ul;
415   }
416   /* ratio */
417   else if (strcmp(varname, "ratio")==0) {
418     ul=strtoul(data, &ptr,0);
419     if (*ptr) return -1;
420     mod_type = _USER_RATIO; user->ratio = ul;
421   }
422   /* tagline */
423   else if (strcmp(varname, "tag")==0) {
424     mod_type = _USER_TAGLINE;
425     strncpy(user->tagline, data, sizeof(user->tagline));
426   }
427   /* uid */ /* FIXME useless ? */
428   /* username (?) */
429   else if (strcmp(varname, "name")==0) {
430     mod_type = _USER_USERNAME;
431     strncpy(user->username, data, sizeof(user->username));
432   }
433   /* user_slots */
434   else if (strcmp(varname, "user_slots")==0) {
435     ul=strtoul(data, &ptr, 0);
436     /* TODO compare with USHORT_MAX */
437     if (*ptr) return -1;
438     mod_type = _USER_USERSLOTS; user->user_slots = (unsigned short)ul;
439   }
440 
441   /* commit to backend */
442   ret = backend_mod_user(config->backends->filename, user->uid, user, mod_type);
443 
444   return ret;
445 }
446 
vars_user_new(const char * username,const char * pass,const char * groupname,wzd_config_t * config)447 int vars_user_new(const char *username, const char *pass, const char *groupname, wzd_config_t * config)
448 {
449   wzd_user_t * newuser;
450   int err;
451 
452   if (!username || !groupname || !config) return -1;
453 
454   newuser = user_create(username,pass,groupname,NULL,config,&err);
455   if (newuser == NULL) return err;
456 
457   /* add it to backend */
458   err = backend_mod_user(config->backends->filename,0,newuser,_USER_CREATE);
459 
460   if (err) { /* problem adding user */
461     user_free(newuser);
462   }
463 
464   return err ? 1 : 0;
465 }
466 
vars_group_get(const char * groupname,const char * varname,void * data,unsigned int datalength,wzd_config_t * config)467 int vars_group_get(const char *groupname, const char *varname, void *data, unsigned int datalength, wzd_config_t * config)
468 {
469   wzd_group_t * group;
470 
471   if (!groupname || !varname) return 1;
472 
473   group = GetGroupByName(groupname);
474   if (!group) return 1;
475 
476   if (strcasecmp(varname,"home")==0) {
477     snprintf(data,datalength,"%s",group->defaultpath);
478     return 0;
479   }
480   if (strcasecmp(varname,"max_dl")==0) {
481     snprintf(data,datalength,"%u",group->max_dl_speed);
482     return 0;
483   }
484   if (strcasecmp(varname,"max_ul")==0) {
485     snprintf(data,datalength,"%u",group->max_ul_speed);
486     return 0;
487   }
488   if (strcasecmp(varname,"name")==0) {
489     snprintf(data,datalength,"%s",group->groupname);
490     return 0;
491   }
492   if (strcasecmp(varname,"tag")==0) {
493     if (group->tagline[0] != '\0')
494       snprintf(data,datalength,"%s",group->tagline);
495     else
496       snprintf(data,datalength,"no tagline set");
497     return 0;
498   }
499 
500   return 1;
501 }
502 
vars_group_set(const char * groupname,const char * varname,const void * data,unsigned int datalength,wzd_config_t * config)503 int vars_group_set(const char *groupname, const char *varname, const void *data, unsigned int datalength, wzd_config_t * config)
504 {
505   wzd_group_t * group;
506   unsigned long mod_type;
507   unsigned long ul;
508   char *ptr;
509   int ret;
510 
511   if (!groupname || !varname) return 1;
512 
513   group = GetGroupByName(groupname);
514   if (!group) return -1;
515 
516   /* find modification type */
517   mod_type = _GROUP_NOTHING;
518 
519   /* groupname */
520   if (strcmp(varname,"name")==0) {
521     mod_type = _GROUP_GROUPNAME;
522     strncpy(group->groupname,data,sizeof(group->groupname));
523     /* NOTE: we do not need to iterate through users, group is referenced
524      * by id, not by name
525      */
526   }
527   /* tagline */
528   else if (strcmp(varname,"tag")==0) {
529     mod_type = _GROUP_TAGLINE;
530     strncpy(group->tagline,data,sizeof(group->tagline));
531   }
532   /* homedir */
533   else if (strcmp(varname,"home")==0) {
534     /* check if homedir exist */
535     {
536       fs_filestat_t s;
537       if (fs_file_stat(data,&s) || !S_ISDIR(s.mode)) {
538         /* Homedir does not exist */
539         return 2;
540       }
541     }
542     mod_type = _GROUP_DEFAULTPATH;
543     strncpy(group->defaultpath,data,WZD_MAX_PATH);
544   }
545   /* max_idle */
546   else if (strcmp(varname,"max_idle")==0) {
547     ul=strtoul(data,&ptr,0);
548     if (!*ptr) { mod_type = _GROUP_IDLE; group->max_idle_time = ul; }
549   }
550   /* perms */
551   else if (strcmp(varname,"perms")==0) {
552     ul=strtoul(data,&ptr,0);
553     if (!*ptr) { mod_type = _GROUP_GROUPPERMS; group->groupperms = ul; }
554   }
555   /* max_ul */
556   else if (strcmp(varname,"max_ul")==0) {
557     ul=strtoul(data,&ptr,0);
558     if (!*ptr) { mod_type = _GROUP_MAX_ULS; group->max_ul_speed = ul; }
559   }
560   /* max_dl */
561   else if (strcmp(varname,"max_dl")==0) {
562     ul=strtoul(data,&ptr,0);
563     if (!*ptr) { mod_type = _GROUP_MAX_DLS; group->max_dl_speed = ul; }
564   }
565   /* num_logins */
566   else if (strcmp(varname,"num_logins")==0) {
567     ul=strtoul(data,&ptr,0);
568     if (!*ptr) { mod_type = _GROUP_NUMLOGINS; group->num_logins = (unsigned short)ul; }
569   }
570   /* ratio */
571   else if (strcmp(varname,"ratio")==0) {
572     ul=strtoul(data,&ptr,0);
573     if (!*ptr) {
574       mod_type = _GROUP_RATIO; group->ratio = ul;
575     }
576   }
577 
578   /* commit to backend */
579   ret = backend_mod_group(config->backends->filename, group->gid, group, mod_type);
580 
581   return ret;
582 }
583 
vars_group_new(const char * groupname,wzd_config_t * config)584 int vars_group_new(const char *groupname, wzd_config_t * config)
585 {
586   int err;
587   wzd_group_t * newgroup;
588 
589   newgroup = group_create(groupname,NULL,config,&err);
590   if (newgroup == NULL) return err;
591 
592   /* add it to backend */
593   err = backend_mod_group(config->backends->filename,0,newgroup,_GROUP_CREATE);
594 
595   if (err) { /* problem adding group */
596     group_free(newgroup);
597   }
598 
599   return (err) ? 1 : 0;
600 }
601 
602 
_str_hash(const char * key)603 static unsigned int _str_hash(const char *key)
604 {
605   const char *p = key;
606   unsigned int h = *p;
607 
608   if (h)
609     for (p += 1; *p != '\0'; p++)
610       h = (h << 5) - h + *p;
611   return h;
612 }
613 
614 
615 
vars_shm_init(void)616 void vars_shm_init(void)
617 {
618   memset(_shm_vars, 0, sizeof(_shm_vars));
619 }
620 
vars_shm_free(void)621 void vars_shm_free(void)
622 {
623   unsigned int i;
624   struct wzd_shm_vars_t * var, * next_var;
625 
626   WZD_MUTEX_LOCK(SET_MUTEX_SHVARS);
627   for (i=0; i<32; i++)
628   {
629     var = _shm_vars[i];
630     _shm_vars[i] = 0;
631 
632     while (var) {
633       if (var->key) {
634         wzd_free(var->key);
635         wzd_free(var->data);
636       }
637 
638       next_var = var->next_var;
639       wzd_free(var);
640       var = next_var;
641     }
642   }
643   WZD_MUTEX_UNLOCK(SET_MUTEX_SHVARS);
644 }
645 
646 /* finds shm entry corresponding to 'varname'
647  * @returns a pointer to the struct or NULL
648  */
vars_shm_find(const char * varname,wzd_config_t * config)649 struct wzd_shm_vars_t * vars_shm_find(const char *varname, wzd_config_t * config)
650 {
651   unsigned int hash;
652   unsigned short index;
653   struct wzd_shm_vars_t * var;
654 
655   hash = _str_hash(varname);
656   index = (hash >> 7) & 31; /* take 5 bits start from the seventh, to give an index in 0 -> 31 */
657 
658   var = _shm_vars[index];
659   while (var)
660   {
661     if (strcmp(var->key, varname)==0)
662       return var;
663   }
664 
665   return NULL;
666 }
667 
668 /* fills data with varname content, max size: datalength
669  * @returns 0 if ok, 1 if an error occured
670  */
vars_shm_get(const char * varname,void * data,unsigned int datalength,wzd_config_t * config)671 int vars_shm_get(const char *varname, void *data, unsigned int datalength, wzd_config_t * config)
672 {
673   struct wzd_shm_vars_t * var;
674   int ret = 1;
675 
676   WZD_MUTEX_LOCK(SET_MUTEX_SHVARS);
677   var = vars_shm_find(varname, config);
678 
679   if (var) {
680     memcpy(data, var->data, MIN(datalength,var->datalength));
681     ret = 0;
682   }
683 
684   WZD_MUTEX_UNLOCK(SET_MUTEX_SHVARS);
685   return ret;
686 }
687 
688 /* change varname with data contents size of data is datalength
689  * Create varname if needed.
690  * @returns 0 if ok, 1 if an error occured
691  */
vars_shm_set(const char * varname,void * data,unsigned int datalength,wzd_config_t * config)692 int vars_shm_set(const char *varname, void *data, unsigned int datalength, wzd_config_t * config)
693 {
694   struct wzd_shm_vars_t * var;
695 
696   WZD_MUTEX_LOCK(SET_MUTEX_SHVARS);
697 
698   var = vars_shm_find(varname, config);
699 
700   if (!var) { /* new variable, must create it */
701     unsigned int hash;
702     unsigned short index;
703 
704     hash = _str_hash(varname);
705     index = (hash >> 7) & 31; /* take 5 bits start from the seventh, to give an index in 0 -> 31 */
706 
707     var = wzd_malloc(sizeof(struct wzd_shm_vars_t));
708     var->key = wzd_strdup(varname);
709     var->data = wzd_malloc(datalength);
710     memcpy(var->data, data, datalength);
711     var->datalength = datalength;
712 
713     /* insertion */
714     var->next_var = _shm_vars[index];
715     _shm_vars[index] = var;
716   } else {
717     /* modification */
718     if (datalength < var->datalength)
719       memcpy(var->data, data, datalength);
720     else { /* need to realloc */
721       var->data = wzd_realloc(var->data, datalength);
722       memcpy(var->data, data, datalength);
723       var->datalength = datalength;
724     }
725   }
726   WZD_MUTEX_UNLOCK(SET_MUTEX_SHVARS);
727 
728   return 0;
729 }
730