• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

proftpd / proftpd / 30135626854

25 Jul 2026 12:15AM UTC coverage: 93.034% (+0.6%) from 92.428%
30135626854

push

github

51363 of 55209 relevant lines covered (93.03%)

219.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.03
/src/stash.c
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 2010-2026 The ProFTPD Project team
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
17
 *
18
 * As a special exemption, The ProFTPD Project and other respective copyright
19
 * holders give permission to link this program with OpenSSL, and distribute
20
 * the resulting executable, without including the source code for OpenSSL in
21
 * the source distribution.
22
 */
23

24
/* Symbol table hashes */
25

26
#include "conf.h"
27

28
#ifndef PR_SYM_POOL_SIZE
29
# define PR_SYM_POOL_SIZE                128
30
#endif /* PR_SYM_POOL_SIZE */
31

32
/* This local structure vastly speeds up symbol lookups. */
33
struct stash {
34
  struct stash *next, *prev;
35
  pool *sym_pool;
36
  unsigned int sym_hash;
37
  const char *sym_name;
38
  size_t sym_namelen;
39
  pr_stash_type_t sym_type;
40
  module *sym_module;
41

42
  union {
43
    conftable *sym_conf;
44
    cmdtable *sym_cmd;
45
    authtable *sym_auth;
46
    cmdtable *sym_hook;
47
    void *sym_generic;
48
  } ptr;
49
};
50

51
static pool *symbol_pool = NULL;
52

53
/* Symbol hashes for each type */
54
static xaset_t *conf_symbol_table[PR_TUNABLE_HASH_TABLE_SIZE];
55
static struct stash *conf_curr_sym = NULL;
56

57
static xaset_t *cmd_symbol_table[PR_TUNABLE_HASH_TABLE_SIZE];
58
static struct stash *cmd_curr_sym = NULL;
59

60
static xaset_t *auth_symbol_table[PR_TUNABLE_HASH_TABLE_SIZE];
61
static struct stash *auth_curr_sym = NULL;
62

63
static xaset_t *hook_symbol_table[PR_TUNABLE_HASH_TABLE_SIZE];
64
static struct stash *hook_curr_sym = NULL;
65

66
/* Symbol stash lookup code and management */
67

68
static struct stash *sym_alloc(void) {
69
  pool *sub_pool;
78✔
70
  struct stash *sym;
78✔
71

78✔
72
  /* XXX Use a smaller pool size, since there are lots of sub-pools allocated
73
   * for Stash symbols.  The default pool size (PR_TUNABLE_POOL_SIZE, 512
74
   * bytes by default) is a bit large for symbols.
75
   */
76
  sub_pool = pr_pool_create_sz(symbol_pool, PR_SYM_POOL_SIZE);
77
  pr_pool_tag(sub_pool, "symbol");
78✔
78

78✔
79
  sym = pcalloc(sub_pool, sizeof(struct stash));
80
  sym->sym_pool = sub_pool;
78✔
81

78✔
82
  return sym;
83
}
78✔
84

85
static int sym_cmp(struct stash *s1, struct stash *s2) {
86
  int res;
11✔
87
  size_t checked_len = 0, namelen;
11✔
88

11✔
89
  if (s1->sym_hash != s2->sym_hash) {
90
    return s1->sym_hash < s2->sym_hash ? -1 : 1;
11✔
91
  }
×
92

93
  if (s1->sym_namelen != s2->sym_namelen) {
94
    return s1->sym_namelen < s2->sym_namelen ? -1 : 1;
11✔
95
  }
×
96

97
  namelen = s1->sym_namelen;
98

11✔
99
  /* Try to avoid strncmp(3) if we can. */
100
  if (namelen >= 2) {
101
    char c1, c2;
11✔
102

11✔
103
    c1 = s1->sym_name[0];
104
    c2 = s2->sym_name[0];
11✔
105

11✔
106
    if (c1 != c2) {
107
      return c1 < c2 ? -1 : 1;
11✔
108
    }
×
109

110
    checked_len++;
111

11✔
112
    if (namelen >= 3) {
113
      c1 = s1->sym_name[1];
11✔
114
      c2 = s2->sym_name[1];
11✔
115

11✔
116
      if (c1 != c2) {
117
        return c1 < c2 ? -1 : 1;
11✔
118
      }
×
119

120
      checked_len++;
121
    }
122
  }
123

124
  res = strncmp(s1->sym_name + checked_len, s2->sym_name + checked_len,
125
    namelen - checked_len);
11✔
126

127
  /* Higher priority modules must go BEFORE lower priority in the
128
   * hash tables.
129
   */
130

131
  if (res == 0) {
132
    if (s1->sym_module != NULL &&
11✔
133
        s2->sym_module != NULL) {
11✔
134

×
135
      if (s1->sym_module->priority > s2->sym_module->priority) {
136
        return -1;
×
137
      }
138

139
      if (s1->sym_module->priority < s2->sym_module->priority) {
140
        return 1;
×
141
      }
142

143
      return res;
144
    }
×
145

146
    if (s1->sym_module != NULL &&
147
        s2->sym_module == NULL) {
11✔
148
      return -1;
×
149
    }
150

151
    if (s1->sym_module == NULL &&
152
        s2->sym_module != NULL) {
11✔
153
      return 1;
11✔
154
    }
155

156
    /* Both sym_module fields are null. */
157
    return 0;
158
  }
11✔
159

160
  return res;
161
}
162

163
static unsigned int symtab_hash(const char *name, size_t namelen) {
164
  register unsigned int i;
288✔
165
  unsigned int h = 0;
288✔
166

288✔
167
  if (name == NULL) {
168
    return 0;
288✔
169
  }
170

171
  for (i = 0; i < namelen; i++) {
172
    const char *cp;
2,737✔
173

2,408✔
174
    cp = (const char *) &(name[i]);
175
    h = (h * 33) + *cp;
2,408✔
176
  }
2,408✔
177

178
  return h;
179
}
180

181
static unsigned int sym_type_hash(pr_stash_type_t sym_type, const char *name,
182
    size_t namelen) {
373✔
183
  unsigned int hash;
184

373✔
185
  if (namelen == 0) {
186
    return 0;
373✔
187
  }
188

189
  /* XXX Ugly hack to support mixed cases of directives in config files. */
190
  if (sym_type != PR_SYM_CONF) {
191
    hash = symtab_hash(name, namelen);
329✔
192

288✔
193
  } else {
194
    register unsigned int i;
195
    char *buf;
41✔
196

41✔
197
    buf = malloc(namelen+1);
198
    if (buf == NULL) {
41✔
199
      pr_log_pri(PR_LOG_ALERT, "Out of memory!");
41✔
200
      exit(1);
×
201
    }
×
202

203
    buf[namelen] = '\0';
204
    for (i = 0; i < namelen; i++) {
41✔
205
      /* Note that the tolower(3) function is locale-sensitive.  This means
382✔
206
       * that setlocale(3) MUST be called before we hash any of the
207
       * configuration directive strings, as when modules are loading, BEFORE
208
       * we process any configuration directives during parsing.  That way,
209
       * we handle the text in a consistent manner for that locale (Bug#4466).
210
       */
211
      buf[i] = tolower((int) name[i]);
212
    }
341✔
213

214
    hash = symtab_hash(buf, namelen);
215
    free(buf);
41✔
216
  }
41✔
217

218
  return hash;
219
}
220

221
int pr_stash_add_symbol(pr_stash_type_t sym_type, void *data) {
222
  struct stash *sym = NULL;
80✔
223
  unsigned int hash;
80✔
224
  int idx = 0;
80✔
225
  xaset_t **symbol_table;
80✔
226
  size_t sym_namelen = 0;
80✔
227

80✔
228
  if (data == NULL) {
229
    errno = EINVAL;
80✔
230
    return -1;
1✔
231
  }
1✔
232

233
  switch (sym_type) {
234
    case PR_SYM_CONF:
79✔
235
      sym = sym_alloc();
15✔
236
      sym->sym_type = PR_SYM_CONF;
15✔
237
      sym->sym_name = ((conftable *) data)->directive;
15✔
238
      sym->sym_module = ((conftable *) data)->m;
15✔
239
      sym->ptr.sym_conf = data;
15✔
240
      symbol_table = conf_symbol_table;
15✔
241
      break;
15✔
242

15✔
243
    case PR_SYM_CMD:
244
      sym = sym_alloc();
16✔
245
      sym->sym_type = PR_SYM_CMD;
16✔
246
      sym->sym_name = ((cmdtable *) data)->command;
16✔
247
      sym->sym_module = ((cmdtable *) data)->m;
16✔
248
      sym->ptr.sym_cmd = data;
16✔
249
      symbol_table = cmd_symbol_table;
16✔
250
      break;
16✔
251

16✔
252
    case PR_SYM_AUTH:
253
      sym = sym_alloc();
37✔
254
      sym->sym_type = PR_SYM_AUTH;
37✔
255
      sym->sym_name = ((authtable *) data)->name;
37✔
256
      sym->sym_module = ((authtable *) data)->m;
37✔
257
      sym->ptr.sym_auth = data;
37✔
258
      symbol_table = auth_symbol_table;
37✔
259
      break;
37✔
260

37✔
261
    case PR_SYM_HOOK:
262
      sym = sym_alloc();
10✔
263
      sym->sym_type = PR_SYM_HOOK;
10✔
264
      sym->sym_name = ((cmdtable *) data)->command;
10✔
265
      sym->sym_module = ((cmdtable *) data)->m;
10✔
266
      sym->ptr.sym_hook = data;
10✔
267
      symbol_table = hook_symbol_table;
10✔
268
      break;
10✔
269

10✔
270
    default:
271
      errno = EINVAL;
1✔
272
      return -1;
1✔
273
  }
1✔
274

275
  /* XXX Should we check for null sym->sym_module as well? */
276
  if (sym->sym_name == NULL) {
277
    destroy_pool(sym->sym_pool);
78✔
278
    errno = EPERM;
4✔
279
    return -1;
4✔
280
  }
4✔
281

282
  sym_namelen = strlen(sym->sym_name);
283
  if (sym_namelen == 0) {
74✔
284
    destroy_pool(sym->sym_pool);
74✔
285
    errno = EPERM;
1✔
286
    return -1;
1✔
287
  }
1✔
288

289
  /* Don't forget to include one for the terminating NUL. */
290
  sym->sym_namelen = sym_namelen + 1;
291

73✔
292
  hash = sym_type_hash(sym_type, sym->sym_name, sym->sym_namelen);
293
  idx = hash % PR_TUNABLE_HASH_TABLE_SIZE;
73✔
294
  sym->sym_hash = hash;
73✔
295

73✔
296
  if (!symbol_table[idx]) {
297
    symbol_table[idx] = xaset_create(symbol_pool, (XASET_COMPARE) sym_cmp);
73✔
298
  }
59✔
299

300
  xaset_insert_sort(symbol_table[idx], (xasetmember_t *) sym, TRUE);
301
  return 0;
73✔
302
}
73✔
303

304
static struct stash *stash_lookup(pr_stash_type_t sym_type,
305
    const char *name, size_t namelen, int idx, unsigned int hash) {
289✔
306
  struct stash *sym = NULL;
307
  xaset_t **symbol_table = NULL;
289✔
308

289✔
309
  switch (sym_type) {
310
    case PR_SYM_CONF:
289✔
311
      symbol_table = conf_symbol_table;
312
      break;
313

314
    case PR_SYM_CMD:
315
      symbol_table = cmd_symbol_table;
27✔
316
      break;
27✔
317

27✔
318
    case PR_SYM_AUTH:
319
      symbol_table = auth_symbol_table;
183✔
320
      break;
183✔
321

183✔
322
    case PR_SYM_HOOK:
323
      symbol_table = hook_symbol_table;
14✔
324
      break;
14✔
325

14✔
326
    default:
327
      errno = EINVAL;
×
328
      return NULL;
×
329
  }
×
330

331
  if (symbol_table[idx]) {
332
    for (sym = (struct stash *) symbol_table[idx]->xas_list; sym;
289✔
333
        sym = sym->next) {
178✔
334
      int res;
×
335

140✔
336
      if (name == NULL) {
337
        break;
140✔
338
      }
339

340
      if (sym->sym_hash != hash) {
341
        continue;
138✔
342
      }
×
343

344
      if (sym->sym_namelen != namelen) {
345
        continue;
138✔
346
      }
×
347

348
      /* Try to avoid strncmp(3) if we can. */
349
      if (namelen >= 1) {
350
        char c1, c2;
138✔
351

138✔
352
        c1 = tolower((int) sym->sym_name[0]);
353
        c2 = tolower((int) name[0]);
138✔
354

138✔
355
        if (c1 != c2) {
356
          continue;
138✔
357
        }
×
358

359
        /* Special case (unlikely, but possible) */
360
        if (namelen == 1 &&
361
            c1 == '\0') {
138✔
362
          break;
138✔
363
        }
364
      }
365

366
      if (namelen >= 2) {
367
        char c1, c2;
138✔
368

138✔
369
        c1 = tolower((int) sym->sym_name[1]);
370
        c2 = tolower((int) name[1]);
138✔
371

138✔
372
        if (c1 != c2) {
373
          continue;
138✔
374
        }
×
375

376
        /* Special case */
377
        if (namelen == 2 &&
378
            c1 == '\0') {
138✔
379
          break;
138✔
380
        }
381
      }
382

383
      res = strncasecmp(sym->sym_name + 2, name + 2, namelen - 2);
384
      if (res == 0) {
138✔
385
        break;
138✔
386
      }
387
    }
388
  }
389

390
  return sym;
391
}
392

393
static struct stash *stash_lookup_next(pr_stash_type_t sym_type,
394
    const char *name, size_t namelen, int idx, unsigned int hash, void *prev) {
39✔
395
  struct stash *sym = NULL;
396
  int last_hit = 0;
39✔
397
  xaset_t **symbol_table = NULL;
39✔
398

39✔
399
  switch (sym_type) {
400
    case PR_SYM_CONF:
39✔
401
      symbol_table = conf_symbol_table;
402
      break;
403

404
    case PR_SYM_CMD:
405
      symbol_table = cmd_symbol_table;
8✔
406
      break;
8✔
407

8✔
408
    case PR_SYM_AUTH:
409
      symbol_table = auth_symbol_table;
17✔
410
      break;
17✔
411

17✔
412
    case PR_SYM_HOOK:
413
      symbol_table = hook_symbol_table;
2✔
414
      break;
2✔
415

2✔
416
    default:
417
      errno = EINVAL;
×
418
      return NULL;
×
419
  }
×
420

421
  if (symbol_table[idx]) {
422
    for (sym = (struct stash *) symbol_table[idx]->xas_list; sym;
39✔
423
        sym = sym->next) {
78✔
424
      if (last_hit) {
39✔
425
        int res;
42✔
426

3✔
427
        if (name == NULL) {
428
          break;
3✔
429
        }
430

431
        if (sym->sym_hash != hash) {
432
          continue;
3✔
433
        }
×
434

435
        if (sym->sym_namelen != namelen) {
436
          continue;
3✔
437
        }
×
438

439
        /* Try to avoid strncmp(3) if we can. */
440
        if (namelen >= 1) {
441
          char c1, c2;
3✔
442

3✔
443
          c1 = tolower((int) sym->sym_name[0]);
444
          c2 = tolower((int) name[0]);
3✔
445

3✔
446
          if (c1 != c2) {
447
            continue;
3✔
448
          }
×
449

450
          /* Special case (unlikely, but possible) */
451
          if (namelen == 1 &&
452
              c1 == '\0') {
3✔
453
            break;
3✔
454
          }
455
        }
456

457
        if (namelen >= 2) {
458
          char c1, c2;
3✔
459

3✔
460
          c1 = tolower((int) sym->sym_name[1]);
461
          c2 = tolower((int) name[1]);
3✔
462

3✔
463
          if (c1 != c2) {
464
            continue;
3✔
465
          }
×
466

467
          /* Special case */
468
          if (namelen == 2 &&
469
              c1 == '\0') {
3✔
470
            break;
3✔
471
          }
472
        }
473

474
        res = strncasecmp(sym->sym_name + 2, name + 2, namelen - 2);
475
        if (res == 0) {
3✔
476
          break;
3✔
477
        }
478
      }
479

480
      if (sym->ptr.sym_generic == prev) {
481
        last_hit++;
39✔
482
      }
39✔
483
    }
484
  }
485

486
  return sym;
487
}
488

489
void *pr_stash_get_symbol2(pr_stash_type_t sym_type, const char *name,
490
    void *prev, int *idx_cache, unsigned int *hash_cache) {
333✔
491
  int idx;
492
  unsigned int hash = 0;
333✔
493
  struct stash *sym = NULL;
333✔
494
  size_t namelen = 0;
333✔
495

333✔
496
  if (sym_type != PR_SYM_CONF &&
497
      sym_type != PR_SYM_CMD &&
333✔
498
      sym_type != PR_SYM_AUTH &&
499
      sym_type != PR_SYM_HOOK) {
333✔
500
    errno = EINVAL;
501
    return NULL;
4✔
502
  }
4✔
503

504
  if (name != NULL) {
505
    /* Don't forget to include one for the terminating NUL. */
329✔
506
    namelen = strlen(name) + 1;
507
  }
285✔
508

509
  if (idx_cache != NULL &&
510
      *idx_cache != -1) {
329✔
511
    idx = *idx_cache;
305✔
512

121✔
513
    if (hash_cache != NULL) {
514
      hash = *hash_cache;
121✔
515
      if (hash == 0) {
121✔
516
        hash = sym_type_hash(sym_type, name, namelen);
121✔
517
        *hash_cache = hash;
42✔
518
      }
42✔
519

520
    } else {
521
      hash = sym_type_hash(sym_type, name, namelen);
522
    }
×
523

524
  } else {
525
    hash = sym_type_hash(sym_type, name, namelen);
526
    idx = hash % PR_TUNABLE_HASH_TABLE_SIZE;
208✔
527

208✔
528
    if (idx_cache != NULL) {
529
      *idx_cache = idx;
208✔
530
    }
184✔
531

532
    if (hash_cache != NULL) {
533
      *hash_cache = hash;
208✔
534
    }
184✔
535
  }
536

537
  if (idx >= PR_TUNABLE_HASH_TABLE_SIZE) {
538
    if (idx_cache != NULL) {
329✔
539
      *idx_cache = -1;
1✔
540
    }
1✔
541

542
    if (hash_cache != NULL) {
543
      *hash_cache = 0;
1✔
544
    }
1✔
545

546
    errno = EINVAL;
547
    return NULL;
1✔
548
  }
1✔
549

550
  if (prev != NULL) {
551
    sym = stash_lookup_next(sym_type, name, namelen, idx, hash, prev);
328✔
552

39✔
553
  } else {
554
    sym = stash_lookup(sym_type, name, namelen, idx, hash);
555
  }
289✔
556

557
  switch (sym_type) {
558
    case PR_SYM_CONF:
328✔
559
      conf_curr_sym = sym;
77✔
560
      if (sym != NULL) {
77✔
561
        return sym->ptr.sym_conf;
77✔
562
      }
17✔
563

564
      errno = ENOENT;
565
      return NULL;
60✔
566

60✔
567
    case PR_SYM_CMD:
568
      cmd_curr_sym = sym;
35✔
569
      if (sym != NULL) {
35✔
570
        return sym->ptr.sym_cmd;
35✔
571
      }
19✔
572

573
      errno = ENOENT;
574
      return NULL;
16✔
575

16✔
576
    case PR_SYM_AUTH:
577
      auth_curr_sym = sym;
200✔
578
      if (sym != NULL) {
200✔
579
        return sym->ptr.sym_auth;
200✔
580
      }
100✔
581

582
      errno = ENOENT;
583
      return NULL;
100✔
584

100✔
585
    case PR_SYM_HOOK:
586
      hook_curr_sym = sym;
16✔
587
      if (sym != NULL) {
16✔
588
        return sym->ptr.sym_hook;
16✔
589
      }
7✔
590

591
      errno = ENOENT;
592
      return NULL;
9✔
593

9✔
594
    default:
595
      /* The expected types are checked at the start of this function. */
596
      break;
597
  }
598

599
  errno = EINVAL;
600
  return NULL;
14✔
601
}
602

14✔
603
void *pr_stash_get_symbol(pr_stash_type_t sym_type, const char *name,
604
    void *prev, int *idx_cache) {
605
  return pr_stash_get_symbol2(sym_type, name, prev, idx_cache, NULL);
7✔
606
}
7✔
607

7✔
608
int pr_stash_remove_conf(const char *directive_name, module *m) {
7✔
609
  int count = 0, prev_idx, symtab_idx = 0;
7✔
610
  size_t directive_namelen = 0;
611
  unsigned int hash;
7✔
612
  conftable *tab = NULL;
1✔
613

1✔
614
  if (directive_name == NULL) {
615
    errno = EINVAL;
616
    return -1;
617
  }
6✔
618

619
  /* Don't forget to include one for the terminating NUL. */
6✔
620
  directive_namelen = strlen(directive_name) + 1;
6✔
621

6✔
622
  hash = sym_type_hash(PR_SYM_CONF, directive_name, directive_namelen);
623
  symtab_idx = hash % PR_TUNABLE_HASH_TABLE_SIZE;
6✔
624
  prev_idx = -1;
625

11✔
626
  tab = pr_stash_get_symbol2(PR_SYM_CONF, directive_name, NULL, &prev_idx,
5✔
627
    &hash);
628
  while (tab) {
629
    pr_signals_handle();
630

631
    /* Note: this works because of a hack: the symbol lookup functions set a
632
     * static pointer, conf_curr_sym, to point to the struct stash just looked
633
     * up.  conf_curr_sym will not be NULL if pr_stash_get_symbol2() returns
634
     * non-NULL.
5✔
635
     */
×
636

5✔
637
    if (m == NULL ||
638
        conf_curr_sym->sym_module == m) {
5✔
639
      xaset_remove(conf_symbol_table[symtab_idx],
5✔
640
        (xasetmember_t *) conf_curr_sym);
5✔
641
      destroy_pool(conf_curr_sym->sym_pool);
5✔
642
      conf_curr_sym = NULL;
643
      tab = NULL;
644
      count++;
5✔
645
    }
646

647
    tab = pr_stash_get_symbol2(PR_SYM_CONF, directive_name, tab, &prev_idx,
648
      &hash);
649
  }
650

651
  return count;
652
}
653

654
/* Sentinel values:
655
 *
656
 *  cmd_type = 0
657
 *  cmd_group = NULL
12✔
658
 *  cmd_class = -1
659
 */
12✔
660
int pr_stash_remove_cmd(const char *cmd_name, module *m,
12✔
661
    unsigned char cmd_type, const char *cmd_group, int cmd_class) {
12✔
662
  int count = 0, prev_idx, symtab_idx = 0;
12✔
663
  size_t cmd_namelen = 0;
664
  unsigned int hash;
12✔
665
  cmdtable *tab = NULL;
1✔
666

1✔
667
  if (cmd_name == NULL) {
668
    errno = EINVAL;
669
    return -1;
670
  }
11✔
671

672
  /* Don't forget to include one for the terminating NUL. */
11✔
673
  cmd_namelen = strlen(cmd_name) + 1;
11✔
674

11✔
675
  hash = sym_type_hash(PR_SYM_CMD, cmd_name, cmd_namelen);
676
  symtab_idx = hash % PR_TUNABLE_HASH_TABLE_SIZE;
11✔
677
  prev_idx = -1;
28✔
678

17✔
679
  tab = pr_stash_get_symbol2(PR_SYM_CMD, cmd_name, NULL, &prev_idx, &hash);
680
  while (tab) {
17✔
681
    cmdtable *cmd_sym;
682

683
    pr_signals_handle();
684

685
    /* Note: this works because of a hack: the symbol lookup functions set a
686
     * static pointer, cmd_curr_sym, to point to the struct stash just looked
687
     * up.  cmd_curr_sym will not be NULL if pr_stash_get_symbol2() returns
688
     * non-NULL.
17✔
689
     */
17✔
690

17✔
691
    cmd_sym = cmd_curr_sym->ptr.sym_cmd;
4✔
692
    if ((m == NULL || cmd_curr_sym->sym_module == m) &&
693
        (cmd_type == 0 || cmd_sym->cmd_type == cmd_type) &&
3✔
694
        (cmd_group == NULL ||
13✔
695
         (cmd_group != NULL &&
3✔
696
          cmd_sym->group != NULL &&
11✔
697
          strcmp(cmd_sym->group, cmd_group) == 0)) &&
698
        (cmd_class == -1 || cmd_sym->cmd_class == cmd_class)) {
11✔
699
      xaset_remove(cmd_symbol_table[symtab_idx],
11✔
700
        (xasetmember_t *) cmd_curr_sym);
11✔
701
      destroy_pool(cmd_curr_sym->sym_pool);
11✔
702
      cmd_curr_sym = NULL;
703
      tab = NULL;
704
      count++;
17✔
705
    }
706

707
    tab = pr_stash_get_symbol2(PR_SYM_CMD, cmd_name, tab, &prev_idx, &hash);
708
  }
709

710
  return count;
29✔
711
}
29✔
712

29✔
713
int pr_stash_remove_auth(const char *api_name, module *m) {
29✔
714
  int count = 0, prev_idx, symtab_idx = 0;
29✔
715
  size_t api_namelen = 0;
716
  unsigned int hash;
29✔
717
  authtable *tab = NULL;
1✔
718

1✔
719
  if (api_name == NULL) {
720
    errno = EINVAL;
721
    return -1;
722
  }
28✔
723

724
  /* Don't forget to include one for the terminating NUL. */
28✔
725
  api_namelen = strlen(api_name) + 1;
28✔
726

28✔
727
  hash = sym_type_hash(PR_SYM_AUTH, api_name, api_namelen);
728
  symtab_idx = hash % PR_TUNABLE_HASH_TABLE_SIZE;
28✔
729
  prev_idx = -1;
56✔
730

28✔
731
  tab = pr_stash_get_symbol2(PR_SYM_AUTH, api_name, NULL, &prev_idx, &hash);
732
  while (tab) {
733
    pr_signals_handle();
734

735
    /* Note: this works because of a hack: the symbol lookup functions set a
736
     * static pointer, auth_curr_sym, to point to the struct stash just looked
737
     * up.  auth_curr_sym will not be NULL if pr_stash_get_symbol2() returns
738
     * non-NULL.
28✔
739
     */
23✔
740

28✔
741
    if (m == NULL ||
742
        auth_curr_sym->sym_module == m) {
28✔
743
      xaset_remove(auth_symbol_table[symtab_idx],
28✔
744
        (xasetmember_t *) auth_curr_sym);
28✔
745
      destroy_pool(auth_curr_sym->sym_pool);
28✔
746
      auth_curr_sym = NULL;
747
      tab = NULL;
748
      count++;
28✔
749
    }
750

751
    tab = pr_stash_get_symbol2(PR_SYM_AUTH, api_name, tab, &prev_idx, &hash);
752
  }
753

754
  return count;
6✔
755
}
6✔
756

6✔
757
int pr_stash_remove_hook(const char *hook_name, module *m) {
6✔
758
  int count = 0, prev_idx, symtab_idx = 0;
6✔
759
  size_t hook_namelen = 0;
760
  unsigned int hash;
6✔
761
  cmdtable *tab = NULL;
1✔
762

1✔
763
  if (hook_name == NULL) {
764
    errno = EINVAL;
765
    return -1;
766
  }
5✔
767

768
  /* Don't forget to include one for the terminating NUL. */
5✔
769
  hook_namelen = strlen(hook_name) + 1;
5✔
770

5✔
771
  hash = sym_type_hash(PR_SYM_HOOK, hook_name, hook_namelen);
772
  symtab_idx = hash % PR_TUNABLE_HASH_TABLE_SIZE;
5✔
773
  prev_idx = -1;
10✔
774

5✔
775
  tab = pr_stash_get_symbol2(PR_SYM_HOOK, hook_name, NULL, &prev_idx, &hash);
776
  while (tab) {
777
    pr_signals_handle();
778

779
    /* Note: this works because of a hack: the symbol lookup functions set a
780
     * static pointer, hook_curr_sym, to point to the struct stash just looked
781
     * up.  hook_curr_sym will not be NULL if pr_stash_get_symbol2() returns
782
     * non-NULL.
5✔
783
     */
×
784

5✔
785
    if (m == NULL ||
786
        hook_curr_sym->sym_module == m) {
5✔
787
      xaset_remove(hook_symbol_table[symtab_idx],
5✔
788
        (xasetmember_t *) hook_curr_sym);
5✔
789
      destroy_pool(hook_curr_sym->sym_pool);
5✔
790
      hook_curr_sym = NULL;
791
      tab = NULL;
792
      count++;
5✔
793
    }
794

795
    tab = pr_stash_get_symbol2(PR_SYM_HOOK, hook_name, tab, &prev_idx, &hash);
796
  }
797

798
  return count;
41✔
799
}
800

41✔
801
int pr_stash_remove_symbol(pr_stash_type_t sym_type, const char *sym_name,
802
    module *sym_module) {
41✔
803
  int count = 0;
4✔
804

4✔
805
  switch (sym_type) {
4✔
806
    case PR_SYM_CONF:
807
      count = pr_stash_remove_conf(sym_name, sym_module);
6✔
808
      break;
6✔
809

6✔
810
    case PR_SYM_CMD:
811
      count = pr_stash_remove_cmd(sym_name, sym_module, 0, NULL, -1);
26✔
812
      break;
26✔
813

26✔
814
    case PR_SYM_AUTH:
815
      count = pr_stash_remove_auth(sym_name, sym_module);
3✔
816
      break;
3✔
817

3✔
818
    case PR_SYM_HOOK:
819
      count = pr_stash_remove_hook(sym_name, sym_module);
2✔
820
      break;
2✔
821

2✔
822
    default:
823
      errno = EINVAL;
824
      return -1;
825
  }
826

827
  return count;
828
}
×
829

×
830
#ifdef PR_USE_DEVEL
×
831
static void stash_dumpf(const char *fmt, ...) {
832
  char buf[PR_TUNABLE_BUFFER_SIZE] = {'\0'};
×
833
  va_list msg;
×
834

×
835
  va_start(msg, fmt);
836
  pr_vsnprintf(buf, sizeof(buf), fmt, msg);
×
837
  va_end(msg);
838

×
839
  buf[sizeof(buf)-1] = '\0';
×
840

841
  pr_log_debug(DEBUG5, "%s", buf);
842
}
843
#endif
12✔
844

845
#ifdef PR_USE_DEVEL
12✔
846
static unsigned int stash_dump_syms(xaset_t **symbol_table, const char *type,
12✔
847
    void (*dumpf)(const char *, ...)) {
848
  register unsigned int i;
492✔
849
  unsigned int count = 0;
480✔
850

480✔
851
  for (i = 0; i < PR_TUNABLE_HASH_TABLE_SIZE; i++) {
480✔
852
    unsigned int nrow_syms = 0;
853
    struct stash *sym;
480✔
854
    xaset_t *syms;
855

480✔
856
    pr_signals_handle();
480✔
857

472✔
858
    syms = symbol_table[i];
859
    if (syms == NULL) {
860
      continue;
12✔
861
    }
4✔
862

863
    for (sym = (struct stash *) syms->xas_list; sym; sym = sym->next) {
864
      nrow_syms++;
8✔
865
    }
866

12✔
867
    dumpf("%s stab index %u: %u symbols", type, i, nrow_syms);
4✔
868

869
    for (sym = (struct stash *) syms->xas_list; sym; sym = sym->next) {
4✔
870
      count++;
1✔
871

872
      if (sym->sym_module != NULL) {
873
        dumpf(" + %s symbol: %s (mod_%s.c)", type, sym->sym_name,
874
          sym->sym_module->name);
3✔
875

876
      } else {
877
        dumpf(" + %s symbol: %s (core)", type, sym->sym_name);
878
      }
879
    }
12✔
880
  }
881

882
  return count;
883
}
3✔
884
#endif /* PR_USE_DEVEL */
885

3✔
886
void pr_stash_dump(void (*dumpf)(const char *, ...)) {
3✔
887
#ifdef PR_USE_DEVEL
888
  unsigned int nsyms = 0, nconf_syms = 0, ncmd_syms = 0, nauth_syms = 0,
3✔
889
    nhook_syms = 0;
×
890

891
  if (dumpf == NULL) {
892
    dumpf = stash_dumpf;
3✔
893
  }
3✔
894

3✔
895
  nconf_syms = stash_dump_syms(conf_symbol_table, "CONF", dumpf);
3✔
896
  ncmd_syms = stash_dump_syms(cmd_symbol_table, "CMD", dumpf);
3✔
897
  nauth_syms = stash_dump_syms(auth_symbol_table, "AUTH", dumpf);
898
  nhook_syms = stash_dump_syms(hook_symbol_table, "HOOK", dumpf);
3✔
899
  nsyms = nconf_syms + ncmd_syms + nauth_syms + nhook_syms;
900

901
  dumpf("stab: %u total symbols: %u CONF, %u CMD, %u AUTH, %u HOOK", nsyms,
3✔
902
    nconf_syms, ncmd_syms, nauth_syms, nhook_syms);
903
#endif /* PR_USE_DEVEL */
73✔
904
}
73✔
905

×
906
int init_stash(void) {
907
  if (symbol_pool != NULL) {
908
    destroy_pool(symbol_pool);
73✔
909
  }
73✔
910

911
  symbol_pool = make_sub_pool(permanent_pool);
73✔
912
  pr_pool_tag(symbol_pool, "Stash Pool");
73✔
913

73✔
914
  memset(conf_symbol_table, '\0', sizeof(conf_symbol_table));
73✔
915
  memset(cmd_symbol_table, '\0', sizeof(cmd_symbol_table));
916
  memset(auth_symbol_table, '\0', sizeof(auth_symbol_table));
73✔
917
  memset(hook_symbol_table, '\0', sizeof(hook_symbol_table));
918

919
  return 0;
920
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc