uid_avp_db: better error handling
[sip-router] / src / modules / uid_avp_db / extra_attrs.c
1 #include "extra_attrs.h"
2 #include "uid_avp_db.h"
3 #include "../../core/usr_avp.h"
4 #include "../../core/sr_module.h"
5 #include "../../core/ut.h"
6 #include "../../core/mem/mem.h"
7 #include "../../core/mem/shm_mem.h"
8 #include "../../core/lock_ops.h"
9 #include "../../core/script_cb.h"
10 #include "../../core/hashes.h"
11
12 #define set_str_val(f,s)        (f).v.lstr=(s); \
13         (f).flags = 0;
14
15 #define set_str_val_ex(f,s)     if ((s).len) { (f).v.lstr=(s); (f).flags = 0; }  \
16         else (f).flags|=DB_NULL; 
17
18 #define set_int_val(f,t)        (f).v.int4=t;\
19         (f).flags=0; 
20
21 #define get_str_val(rvi,dst)    do{if(!(rvi.flags&DB_NULL)){dst=rvi.v.lstr;} else dst.len = 0;}while(0)
22 #define get_int_val(rvi,dst)    do{if(!(rvi.flags&DB_NULL)){dst=rvi.v.int4;} else dst = 0;}while(0)
23
24 typedef struct _registered_table_t {
25         char *id;
26         char *table_name;
27         
28         /* column names */
29         char *key_column;
30         char *name_column;
31         char *type_column;
32         char *value_column;
33         char *flags_column;
34
35         char *flag_name;
36
37         /* pregenerated queries */
38         db_cmd_t *query;
39         db_cmd_t *remove;
40         db_cmd_t *add;
41
42         avp_flags_t flag;
43
44         int group_mutex_idx;
45
46         struct _registered_table_t *next;
47
48         char buf[1]; /* buffer for strings allocated with the structure */
49 } registered_table_t;
50
51 static registered_table_t *tables = NULL;
52
53 registered_table_t *find_registered_table(const char *id)
54 {
55         registered_table_t *t = tables;
56         while (t) {
57                 if (strcmp(t->id, id) == 0) return t;
58                 t = t->next;
59         }
60         return NULL;
61 }
62
63 char *get_token(char *s, str *name, str *value)
64 {
65         enum { reading_name, reading_value } state = reading_name;
66         /* returns 'token' which has the form name[=value][,]
67          * replaces separators ,= by binary 0 to allow char* strings */
68
69         name->s = s;
70         name->len = 0;
71         value->s = NULL;
72         value->len = 0;
73
74         while (*s) {
75                 switch (state) {
76                         case reading_name: 
77                                 switch (*s) {
78                                         case '=':
79                                         case ':': 
80                                                 state = reading_value;
81                                                 value->s = s + 1;
82                                                 *s = 0; /* replace separator */
83                                                 break;
84                                         case ',': 
85                                                 *s = 0; /* replace separator */
86                                                 return s + 1;
87                                         default: name->len++;
88                                 }
89                                 break;
90                         case reading_value:
91                                 if (*s == ',') {
92                                         *s = 0; /* replace separator */
93                                         return s + 1;
94                                 }
95                                 else value->len++;
96                                 break;
97                 }
98                 s++;
99         }
100         return NULL; /* everything read */
101 }
102
103 static int cmp_s(str *a, str *b)
104 {
105         int i;
106         /* Warning: none string can be NULL! */
107         if (a->len != b->len) return -1;
108         if (!a->len) return 0; /* equal - empty */
109         for (i = 0; i < a->len; i++) 
110                 if (a->s[i] != b->s[i]) return 1;
111         return 0;
112 }
113
114 static inline void cpy(char *dst, str *s)
115 {
116         memcpy(dst, s->s, s->len);
117         dst[s->len] = 0;
118 }
119
120 /* adds new 'extra attribute group' (it adds new table for it) */
121 int declare_attr_group(modparam_t type, char* _param)
122 {
123         registered_table_t *rt;
124         str name, value;
125         str param;
126         char *p;
127         
128         static str table = STR_STATIC_INIT("table");
129         static str flag = STR_STATIC_INIT("flag");
130         static str id = STR_STATIC_INIT("id");
131         static str key_column = STR_STATIC_INIT("key_column");
132         static str name_column = STR_STATIC_INIT("name_column");
133         static str value_column = STR_STATIC_INIT("value_column");
134         static str type_column = STR_STATIC_INIT("type_column");
135         static str flags_column = STR_STATIC_INIT("flags_column");
136         
137         if (!(type & PARAM_STR)) {
138                 ERR("Invalid parameter type\n");
139                 return -1;
140         }
141         if (!_param) {
142                 ERR("invalid parameter value\n");
143                 return -1;
144         }
145         param = *((str*)_param);
146     DBG("group def: %.*s\n", param.len, param.s);
147         
148         rt = pkg_malloc(param.len + sizeof(*rt) + 1);
149         if (!rt) {
150                 ERR("can't allocate PKG memory\n");
151                 return -1;
152         }
153         memset(rt, 0, sizeof(*rt));
154         cpy(rt->buf, &param);
155
156         /* default column names */
157         rt->key_column = "id";
158         rt->name_column = "name";
159         rt->type_column = "type";
160         rt->value_column = "value";
161         rt->flags_column = "flags";
162
163         /* parse the string */
164         p = rt->buf;
165         do {
166                 p = get_token(p, &name, &value);
167                 if (cmp_s(&name, &table) == 0) rt->table_name = value.s;
168                 else if (cmp_s(&name, &flag) == 0) rt->flag_name = value.s;
169                 else if (cmp_s(&name, &id) == 0) rt->id = value.s;
170                 else if (cmp_s(&name, &key_column) == 0) rt->key_column = value.s;
171                 else if (cmp_s(&name, &name_column) == 0) rt->name_column = value.s;
172                 else if (cmp_s(&name, &type_column) == 0) rt->type_column = value.s;
173                 else if (cmp_s(&name, &value_column) == 0) rt->value_column = value.s;
174                 else if (cmp_s(&name, &flags_column) == 0) rt->flags_column = value.s;
175         } while (p);
176         
177         if ((!rt->id) || (!rt->flag_name)) {
178                 ERR("at least attribute group ID and flags must be given\n");
179                 pkg_free(rt);
180                 return -1;
181         }
182         /* insert new element into registered tables */
183
184         rt->flag = register_avpflag(rt->flag_name);
185         if (!rt->flag) {
186                 ERR("can't register AVP flag: %s\n", rt->flag_name);
187                 pkg_free(rt);
188                 return -1;
189         }
190         
191         /* append to the beggining - it doesn't depend on the order */
192         rt->next = tables;
193         tables = rt;
194
195         return 0;
196 }
197
198 /** Initialize all queries needed by 'extra attributes' for given table. 
199  * Variable default_res holds columns which can used by read_attrs. */
200 static int init_queries(db_ctx_t *ctx, registered_table_t *t)
201 {
202         db_fld_t match[] = {
203                 { .name = t->key_column, .type = DB_STR, .op = DB_EQ },
204                 { .name = NULL }
205         };
206         db_fld_t query_res[] = {
207                 /* Warning: be careful here - the query must have the same result
208                  * as query for user/uri AVPs to be readable by read_attrs */
209                 { .name = t->name_column, .type = DB_STR, .op = DB_EQ },
210                 { .name = t->type_column, .type = DB_INT, .op = DB_EQ },
211                 { .name = t->value_column, .type = DB_STR, .op = DB_EQ },
212                 { .name = t->flags_column, .type = DB_BITMAP, .op = DB_EQ },
213                 { .name = NULL }
214         };
215         db_fld_t add_values[] = {
216                 { .name = t->key_column, .type = DB_STR, .op = DB_EQ },
217                 { .name = t->name_column, .type = DB_STR, .op = DB_EQ },
218                 { .name = t->type_column, .type = DB_INT, .op = DB_EQ },
219                 { .name = t->value_column, .type = DB_STR, .op = DB_EQ },
220                 { .name = t->flags_column, .type = DB_BITMAP, .op = DB_EQ },
221                 { .name = NULL }
222         };
223
224         t->query = db_cmd(DB_GET, ctx, t->table_name, query_res, match, NULL);
225         t->remove = db_cmd(DB_DEL, ctx, t->table_name, NULL, match, NULL);
226         t->add = db_cmd(DB_PUT, ctx, t->table_name, NULL, NULL, add_values);
227
228         if (t->query && t->remove && t->add) return 0;
229         else return -1; /* not all queries were initialized */
230 }
231
232 int init_extra_avp_queries(db_ctx_t *ctx)
233 {
234         registered_table_t *t = tables;
235         while (t) {
236                 if (init_queries(ctx, t) < 0)  return -1;
237                 t = t->next;
238         }
239         return 0;
240 }
241
242 static void get_avp_value_ex(avp_t *avp, str *dst, int *type) {
243         avp_value_t val;
244
245         /* Warning! it uses static buffer from int2str !!! */
246
247         get_avp_val(avp, &val);
248         if (avp->flags & AVP_VAL_STR) {
249                 *dst = val.s;
250                 *type = AVP_VAL_STR;
251         }
252         else { /* probably (!) number */
253                 dst->s = int2str(val.n, &dst->len);
254                 *type = 0;
255         }
256 }
257
258 /** Saves attribute into DB with given ID.  The ID must not be NULL. The
259  * use_table must be called outside of this function (from interface
260  * functions) */
261 static inline int save_avp(registered_table_t *t, avp_t *avp, str *id) /* id MUST NOT be NULL */
262 {
263         str *s, v;
264         int type;
265         static str empty = STR_STATIC_INIT("");
266         
267         set_str_val(t->add->vals[0], *id);
268         
269         s = get_avp_name(avp);
270         if (!s) s = &empty;
271         set_str_val(t->add->vals[1], *s);
272         
273         get_avp_value_ex(avp, &v, &type);
274         set_int_val(t->add->vals[2], type);
275         set_str_val(t->add->vals[3], v);
276         set_int_val(t->add->vals[4], avp->flags & (AVP_CLASS_ALL | AVP_TRACK_ALL | AVP_NAME_STR | AVP_VAL_STR));
277         
278         if (db_exec(NULL, t->add) < 0) {
279                 ERR("Can't insert record into DB\n");
280                 return -1;
281         }
282         return 0;
283 }
284
285 /** Loads all attributes with given ID.  The ID must not be NULL. */
286 static int read_avps(db_res_t *res, avp_flags_t flag) /* id must not be NULL */
287 {
288         db_rec_t *row;
289
290         row = db_first(res);
291         while (row) {
292                 int flags = 0;
293                 int type = 0;
294                 str value = STR_NULL;
295                 avp_value_t val;
296                 avp_name_t name;
297                 
298                 get_str_val(row->fld[0], name.s);
299                 get_int_val(row->fld[1], type);
300                 get_str_val(row->fld[2], value);
301                 get_int_val(row->fld[3], flags);
302
303                 if (flags & SRDB_LOAD_SER) {
304                         if (type == AVP_VAL_STR) val.s = value;
305                         else str2int(&value, (unsigned int *)&val.n); /* FIXME */
306
307                         flags |= flag;
308
309                         /* FIXME: avps probably should be removed before they are added,
310                          * but this should be done in add_avp and not here! */
311                         add_avp(flags, name, val);
312                 }
313                 row = db_next(res);
314         }
315         return 0;
316 }
317
318 /** Removes all attributes with given ID.  The ID must not be NULL.  */
319 static inline int remove_all_avps(registered_table_t *t, str *id)
320 {
321         set_str_val(t->remove->match[0], *id);
322         if (db_exec(NULL, t->remove) < 0) {
323                 ERR("can't remove attrs\n");
324                 return -1;
325         }
326         return 0;
327 }
328
329 /* ----- interface functions ----- */
330
331 int load_extra_attrs(struct sip_msg* msg, char* _table, char* _id)
332 {
333         registered_table_t *t;
334         db_res_t *res = NULL;
335         str id;
336
337         t = (registered_table_t *)_table;
338         if ((!t) || (get_str_fparam(&id, msg, (fparam_t*)_id) < 0)) {
339                 ERR("invalid parameter value\n");
340                 return -1;
341         }
342         
343         set_str_val(t->query->match[0], id);
344         if (db_exec(&res, t->query) < 0) {
345                 ERR("DB query failed\n");
346                 return -1;
347         }
348         if (res) {
349                 read_avps(res, t->flag);
350                 db_res_free(res);
351         }
352
353         return 1;
354 }
355
356 int remove_extra_attrs(struct sip_msg* msg, char *_table, char* _id)
357 {
358         str id;
359         registered_table_t *t;
360
361         t = (registered_table_t *)_table;
362
363         if ((!t) || (get_str_fparam(&id, msg, (fparam_t*)_id) < 0)) {
364                 ERR("invalid parameter value\n");
365                 return -1;
366         }
367         remove_all_avps(t, &id);
368
369         return 1;
370 }
371
372 int save_extra_attrs(struct sip_msg* msg, char* _table, char *_id)
373 {
374         str id;
375         int i;
376         struct usr_avp *avp;
377         static unsigned short lists[] = {
378                 AVP_CLASS_USER | AVP_TRACK_FROM, 
379                 AVP_CLASS_USER | AVP_TRACK_TO, 
380                 AVP_CLASS_URI | AVP_TRACK_FROM, 
381                 AVP_CLASS_URI | AVP_TRACK_TO, 
382                 0
383         };
384         registered_table_t *t;
385
386         t = (registered_table_t *)_table;
387
388         if ((!t) || (get_str_fparam(&id, msg, (fparam_t*)_id) < 0)) {
389                 ERR("invalid parameter value\n");
390                 return -1;
391         }
392
393         /* delete all attrs under given id */
394         remove_all_avps(t, &id);
395
396         /* save all attrs flagged with flag under id */ 
397         for (i = 0; lists[i]; i++) {
398                 for (avp = get_avp_list(lists[i]); avp; avp = avp->next) {
399                         if ((avp->flags & t->flag) != 0) save_avp(t, avp, &id);
400                 }
401         }
402         return 1;
403 }
404
405 int extra_attrs_fixup(void** param, int param_no)
406 {
407         registered_table_t *t;
408
409         switch (param_no) {
410                 case 1: /* try to find registered table, error if not found */
411                                 t = find_registered_table(*param);
412                                 if (!t) {
413                                         ERR("can't find attribute group with id: %s\n", (char*)*param);
414                                         return -1;
415                                 }
416                                 *param = (void*)t;
417                                 break;
418                 case 2: return fixup_var_str_2(param, param_no);
419         }
420         return 0;
421 }
422
423 /******* locking *******/ 
424
425 #define LOCK_CNT        32
426
427 gen_lock_t *locks = NULL; /* set of mutexes allocated in shared memory */
428 int lock_counters[LOCK_CNT]; /* set of counters (each proces has its own counters) */
429
430 static int avpdb_post_script_cb(struct sip_msg *msg, unsigned int flags, void *param) {
431         int i;
432
433         for (i=0; i<LOCK_CNT; i++) {
434                 if (lock_counters[i] > 0) {
435                         if (auto_unlock) {
436                                 DEBUG("post script auto unlock extra attrs <%d>\n", i);
437                                 lock_release(&locks[i]);
438                                 lock_counters[i]=0;
439                         } else {
440                                 BUG("script writer didn't unlock extra attrs !!!\n");
441                                 return 1;
442                         }
443                 }
444         }
445         return 1;
446 }
447
448 int init_extra_avp_locks()
449 {
450         int i;
451         registered_table_t *t = tables;
452
453         if(register_script_cb(avpdb_post_script_cb,
454                         REQUEST_CB | ONREPLY_CB | POST_SCRIPT_CB, 0)<0) {
455                 LM_ERR("failed to register script callbacks\n");
456                 return -1;
457         }
458
459         /* zero all 'lock counters' */
460         memset(lock_counters, 0, sizeof(lock_counters));
461
462         locks = shm_malloc(sizeof(gen_lock_t) * LOCK_CNT);
463         if (!locks) {
464                 ERR("can't allocate mutexes\n");
465                 return -1;
466         }
467         for (i = 0; i < LOCK_CNT; i++) {
468                 lock_init(&locks[i]);
469         }
470
471         /* initializes mutexes for extra AVPs */
472         i = 0;
473         while (t) {
474                 t->group_mutex_idx = get_hash1_raw(t->table_name, strlen(t->table_name)) % LOCK_CNT;
475                 t = t->next;
476         }
477
478         return 0;
479 }
480
481 static inline int find_mutex(registered_table_t *t, str *id)
482 {
483         /* hash(table_name) + hash(id) */
484         return ((t->group_mutex_idx + get_hash1_raw(id->s, id->len)) % LOCK_CNT);
485 }
486
487 int lock_extra_attrs(struct sip_msg* msg, char *_table, char* _id)
488 {
489         str id;
490         registered_table_t *t;
491         int mutex_idx;
492
493         t = (registered_table_t *)_table;
494         if ((!t) || (get_str_fparam(&id, msg, (fparam_t*)_id) < 0)) {
495                 ERR("invalid parameter value\n");
496                 return -1;
497         }
498
499         /* find right mutex according to id/table */
500         mutex_idx = find_mutex(t, &id);
501
502         if (lock_counters[mutex_idx] > 0) {
503                 /* mutex is already locked by this process */
504                 lock_counters[mutex_idx]++;
505         }
506         else {
507                 /* the mutex was not locked => lock it and set counter */
508                 lock_get(&locks[mutex_idx]);
509                 lock_counters[mutex_idx] = 1;
510         }
511
512         return 1;
513 }
514
515 int unlock_extra_attrs(struct sip_msg* msg, char *_table, char* _id)
516 {
517         str id;
518         registered_table_t *t;
519         int mutex_idx;
520
521         t = (registered_table_t *)_table;
522         if ((!t) || (get_str_fparam(&id, msg, (fparam_t*)_id) < 0)) {
523                 ERR("invalid parameter value\n");
524                 return -1;
525         }
526
527         /* find right mutex according to id/table */
528         mutex_idx = find_mutex(t, &id);
529
530         if (lock_counters[mutex_idx] > 1) {
531                 /* mutex is locked more times by this process */
532                 lock_counters[mutex_idx]--;
533         }
534         else if (lock_counters[mutex_idx] == 1) {
535                 /* the mutex is locked once => unlock it and reset counter */
536                 lock_release(&locks[mutex_idx]);
537                 lock_counters[mutex_idx] = 0;
538         }
539         else {
540                 BUG("trying to unlock without lock group=\"%s\" id=\"%.*s\"\n", t->id, id.len, id.s);
541         }
542
543         return 1;
544 }
545