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

proftpd / proftpd / 14526507026

17 Apr 2025 11:25PM UTC coverage: 93.03% (+0.4%) from 92.667%
14526507026

push

github

51358 of 55206 relevant lines covered (93.03%)

234.02 hits per line

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

90.52
/src/modules.c
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 1997, 1998 Public Flood Software
4
 * Copyright (c) 2001-2023 The ProFTPD Project team
5
 *
6
 * This program is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 2 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
19
 *
20
 * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
21
 * and other respective copyright holders give permission to link this program
22
 * with OpenSSL, and distribute the resulting executable, without including
23
 * the source code for OpenSSL in the source distribution.
24
 */
25

26
/* Module handling routines */
27

28
#include "conf.h"
29

30
extern module *static_modules[];
31
extern module *loaded_modules;
32

33
/* Currently running module */
34
module *curr_module = NULL;
35

36
/* Used to track the priority for loaded modules. */
37
static unsigned int curr_module_pri = 0;
38

39
static const char *trace_channel = "module";
40

41
modret_t *pr_module_call(module *m, modret_t *(*func)(cmd_rec *),
86✔
42
    cmd_rec *cmd) {
43
  modret_t *res;
86✔
44
  module *prev_module = curr_module;
86✔
45

46
  if (m == NULL ||
86✔
47
      func == NULL ||
86✔
48
      cmd == NULL) {
49
    errno = EINVAL;
7✔
50
    return NULL;
7✔
51
  }
52

53
  if (cmd->tmp_pool == NULL) {
79✔
54
    cmd->tmp_pool = make_sub_pool(cmd->pool);
9✔
55
    pr_pool_tag(cmd->tmp_pool, "Module call tmp_pool");
9✔
56
  }
57

58
  curr_module = m;
79✔
59
  res = func(cmd);
79✔
60
  curr_module = prev_module;
79✔
61

62
  /* Note that we don't clear the pool here because the function may
63
   * return data which resides in this pool.
64
   */
65
  return res;
79✔
66
}
67

68
modret_t *mod_create_data(cmd_rec *cmd, void *d) {
29✔
69
  modret_t *res;
29✔
70

71
  if (cmd == NULL) {
29✔
72
    errno = EINVAL;
1✔
73
    return NULL;
1✔
74
  }
75

76
  res = pcalloc(cmd->tmp_pool, sizeof(modret_t));
28✔
77
  res->data = d;
28✔
78

79
  return res;
28✔
80
}
81

82
modret_t *mod_create_ret(cmd_rec *cmd, unsigned char err, const char *n,
75✔
83
    const char *m) {
84
  modret_t *res;
75✔
85

86
  if (cmd == NULL) {
75✔
87
    errno = EINVAL;
1✔
88
    return NULL;
1✔
89
  }
90

91
  res = pcalloc(cmd->tmp_pool, sizeof(modret_t));
74✔
92
  res->mr_handler_module = curr_module;
74✔
93
  res->mr_error = err;
74✔
94

95
  if (n != NULL) {
74✔
96
    res->mr_numeric = pstrdup(cmd->tmp_pool, n);
1✔
97
  }
98

99
  if (m != NULL) {
74✔
100
    res->mr_message = pstrdup(cmd->tmp_pool, m);
54✔
101
  }
102

103
  return res;
104
}
105

106
modret_t *mod_create_error(cmd_rec *cmd, int mr_errno) {
20✔
107
  modret_t *res;
20✔
108

109
  if (cmd == NULL) {
20✔
110
    errno = EINVAL;
1✔
111
    return NULL;
1✔
112
  }
113

114
  res = pcalloc(cmd->tmp_pool, sizeof(modret_t));
19✔
115
  res->mr_handler_module = curr_module;
19✔
116
  res->mr_error = mr_errno;
19✔
117

118
  return res;
19✔
119
}
120

121
/* Called after forking in order to inform/initialize modules
122
 * need to know we are a child and have a connection.
123
 */
124
int modules_session_init(void) {
4✔
125
  module *prev_module = curr_module, *m;
4✔
126

127
  for (m = loaded_modules; m; m = m->next) {
6✔
128
    if (m->sess_init) {
3✔
129
      curr_module = m;
2✔
130

131
      pr_trace_msg(trace_channel, 12,
2✔
132
        "invoking sess_init callback on mod_%s.c", m->name);
133
      if (m->sess_init() < 0) {
2✔
134
        int xerrno = errno;
1✔
135

136
        pr_log_pri(PR_LOG_WARNING,
1✔
137
          "mod_%s.c: error initializing session (%s), check module logs "
138
          "for details", m->name, strerror(xerrno));
139

140
        errno = xerrno;
1✔
141
        return -1;
1✔
142
      }
143
    }
144
  }
145

146
  curr_module = prev_module;
3✔
147
  return 0;
3✔
148
}
149

150
unsigned char command_exists(const char *name) {
1✔
151
  int idx = -1;
1✔
152
  unsigned int hash = 0;
1✔
153
  cmdtable *cmdtab;
1✔
154

155
  cmdtab = pr_stash_get_symbol2(PR_SYM_CMD, name, NULL, &idx, &hash);
1✔
156
  while (cmdtab && cmdtab->cmd_type != CMD) {
1✔
157
    pr_signals_handle();
×
158
    cmdtab = pr_stash_get_symbol2(PR_SYM_CMD, name, cmdtab, &idx, &hash);
×
159
  }
160

161
  return (cmdtab ? TRUE : FALSE);
1✔
162
}
163

164
unsigned char pr_module_exists(const char *name) {
5✔
165
  return pr_module_get(name) != NULL ? TRUE : FALSE;
5✔
166
}
167

168
module *pr_module_get(const char *name) {
52✔
169
  char buf[80] = {'\0'};
52✔
170
  module *m;
52✔
171

172
  if (name == NULL) {
52✔
173
    errno = EINVAL;
2✔
174
    return NULL;
2✔
175
  }
176

177
  /* Check the list of compiled-in modules. */
178
  for (m = loaded_modules; m; m = m->next) {
58✔
179
    memset(buf, '\0', sizeof(buf));
32✔
180
    pr_snprintf(buf, sizeof(buf), "mod_%s.c", m->name);
32✔
181
    buf[sizeof(buf)-1] = '\0';
32✔
182

183
    if (strcmp(buf, name) == 0) {
32✔
184
      return m;
24✔
185
    }
186
  }
187

188
  errno = ENOENT;
26✔
189
  return NULL;
26✔
190
}
191

192
void modules_list2(int (*listf)(const char *, ...), int flags) {
4✔
193
  if (listf == NULL) {
4✔
194
    listf = printf;
1✔
195
  }
196

197
  if (flags & PR_MODULES_LIST_FL_SHOW_STATIC) {
4✔
198
    register unsigned int i = 0;
2✔
199

200
    listf("Compiled-in modules:\n");
2✔
201
    for (i = 0; static_modules[i]; i++) {
4✔
202
      module *m = static_modules[i];
×
203

204
      if (flags & PR_MODULES_LIST_FL_SHOW_VERSION) {
×
205
        const char *version;
×
206

207
        version = m->module_version;
×
208
        if (version != NULL) {
×
209
          listf("  %s\n", version);
×
210

211
        } else {
212
          listf("  mod_%s.c\n", m->name);
×
213
        }
214

215
      } else {
216
        listf("  mod_%s.c\n", m->name);
×
217
      }
218
    }
219

220
  } else {
221
    module *m;
2✔
222

223
    listf("Loaded modules:\n");
2✔
224
    for (m = loaded_modules; m; m = m->next) {
4✔
225

226
      if (flags & PR_MODULES_LIST_FL_SHOW_VERSION) {
2✔
227
        const char *version;
2✔
228

229
        version = m->module_version;
2✔
230
        if (version != NULL) {
2✔
231
          listf("  %s\n", version);
1✔
232

233
        } else {
234
          listf("  mod_%s.c\n", m->name);
1✔
235
        }
236

237
      } else {
238
        listf("  mod_%s.c\n", m->name);
×
239
      }
240
    }
241
  }
242
}
4✔
243

244
void modules_list(int flags) {
1✔
245
  modules_list2(NULL, flags);
1✔
246
}
1✔
247

248
int pr_module_load_authtab(module *m) {
7✔
249
  if (m == NULL ||
7✔
250
      m->name == NULL) {
6✔
251
    errno = EINVAL;
2✔
252
    return -1;
2✔
253
  }
254

255
  if (m->authtable) {
5✔
256
    authtable *authtab;
257

258
    for (authtab = m->authtable; authtab->name; authtab++) {
2✔
259
      authtab->m = m;
1✔
260

261
      if (pr_stash_add_symbol(PR_SYM_AUTH, authtab) < 0) {
1✔
262
        return -1;
263
      }
264
    }
265
  }
266

267
  return 0;
268
}
269

270
int pr_module_load_cmdtab(module *m) {
7✔
271
  if (m == NULL ||
7✔
272
      m->name == NULL) {
6✔
273
    errno = EINVAL;
2✔
274
    return -1;
2✔
275
  }
276

277
  if (m->cmdtable) {
5✔
278
    cmdtable *cmdtab;
279

280
    for (cmdtab = m->cmdtable; cmdtab->command; cmdtab++) {
3✔
281
      cmdtab->m = m;
2✔
282

283
      if (cmdtab->cmd_type == HOOK) {
2✔
284
        if (pr_stash_add_symbol(PR_SYM_HOOK, cmdtab) < 0) {
1✔
285
          return -1;
286
        }
287

288
      } else {
289
        /* All other cmd_types are for CMDs: PRE_CMD, CMD, POST_CMD, etc. */
290
        if (pr_stash_add_symbol(PR_SYM_CMD, cmdtab) < 0) {
1✔
291
          return -1;
292
        }
293
      }
294
    }
295
  }
296

297
  return 0;
298
}
299

300
int pr_module_load_conftab(module *m) {
9✔
301
  if (m == NULL ||
9✔
302
      m->name == NULL) {
8✔
303
    errno = EINVAL;
2✔
304
    return -1;
2✔
305
  }
306

307
  if (m->conftable) {
7✔
308
    conftable *conftab;
309

310
    for (conftab = m->conftable; conftab->directive; conftab++) {
8✔
311
      conftab->m = m;
5✔
312

313
      if (pr_stash_add_symbol(PR_SYM_CONF, conftab) < 0) {
5✔
314
        return -1;
315
      }
316
    }
317
  }
318

319
  return 0;
320
}
321

322
int pr_module_load(module *m) {
8✔
323
  char buf[256];
8✔
324

325
  if (m == NULL ||
8✔
326
      m->name == NULL) {
7✔
327
    errno = EINVAL;
2✔
328
    return -1;
2✔
329
  }
330

331
  /* Check the API version the module wants to use. */
332
  if (m->api_version < PR_MODULE_API_VERSION) {
6✔
333
    errno = EACCES;
1✔
334
    return -1;
1✔
335
  }
336

337
  /* Do not allow multiple modules with the same name. */
338
  memset(buf, '\0', sizeof(buf));
5✔
339
  pr_snprintf(buf, sizeof(buf), "mod_%s.c", m->name);
5✔
340
  buf[sizeof(buf)-1] = '\0';
5✔
341

342
  if (pr_module_get(buf) != NULL) {
5✔
343
    errno = EEXIST;
1✔
344
    return -1;
1✔
345
  }
346

347
  /* Invoke the module's initialization routine. */
348
  if (!m->init ||
5✔
349
      m->init() >= 0) {
1✔
350

351
    /* Assign a priority to this module. */
352
    m->priority = curr_module_pri++;
3✔
353

354
    /* Add the module's config, cmd, and auth tables. */
355
    if (pr_module_load_conftab(m) < 0) {
3✔
356
      return -1;
357
    }
358

359
    if (pr_module_load_cmdtab(m) < 0) {
3✔
360
      return -1;
361
    }
362

363
    if (pr_module_load_authtab(m) < 0) {
3✔
364
      return -1;
365
    }
366

367
    /* Add the module to the loaded_modules list. */
368
    if (loaded_modules) {
3✔
369
      m->next = loaded_modules;
×
370
      loaded_modules->prev = m;
×
371
    }
372

373
    loaded_modules = m;
3✔
374

375
    /* Generate an event. */
376
    pr_event_generate("core.module-load", buf);
3✔
377
    return 0;
3✔
378
  }
379

380
  errno = EPERM;
1✔
381
  return -1;
1✔
382
}
383

384
int pr_module_unload(module *m) {
16✔
385
  char buf[256];
16✔
386

387
  if (m == NULL ||
16✔
388
      m->name == NULL) {
15✔
389
    errno = EINVAL;
2✔
390
    return -1;
2✔
391
  }
392

393
  /* Make sure this module has been loaded.  We can't unload a module that
394
   * has not been loaded, now can we?
395
   */
396

397
  memset(buf, '\0', sizeof(buf));
14✔
398
  pr_snprintf(buf, sizeof(buf), "mod_%s.c", m->name);
14✔
399
  buf[sizeof(buf)-1] = '\0';
14✔
400

401
  if (pr_module_get(buf) == NULL) {
14✔
402
    errno = ENOENT;
10✔
403
    return -1;
10✔
404
  }
405

406
  /* Generate an event. */
407
  pr_event_generate("core.module-unload", buf);
4✔
408

409
  /* Remove the module from the loaded_modules list. */
410
  if (m->prev) {
4✔
411
    m->prev->next = m->next;
×
412

413
  } else {
414
    /* This module is the start of the loaded_modules list (prev is NULL),
415
     * so we need to update that pointer, too.
416
     */
417
    loaded_modules = m->next;
4✔
418
  }
419

420
  if (m->next) {
4✔
421
    m->next->prev = m->prev;
×
422
  }
423

424
  m->prev = m->next = NULL;
4✔
425

426
  /* Remove the module's config, cmd, and auth tables. */
427
  if (m->conftable) {
4✔
428
    conftable *conftab;
429

430
    for (conftab = m->conftable; conftab->directive; conftab++) {
2✔
431
      pr_stash_remove_symbol(PR_SYM_CONF, conftab->directive, conftab->m);
1✔
432
    }
433
  }
434

435
  if (m->cmdtable) {
4✔
436
    cmdtable *cmdtab;
437

438
    for (cmdtab = m->cmdtable; cmdtab->command; cmdtab++) {
3✔
439
      if (cmdtab->cmd_type == HOOK) {
2✔
440
        pr_stash_remove_symbol(PR_SYM_HOOK, cmdtab->command, cmdtab->m);
1✔
441

442
      } else {
443
        /* All other cmd_types are for CMDs: PRE_CMD, CMD, POST_CMD, etc. */
444
        pr_stash_remove_symbol(PR_SYM_CMD, cmdtab->command, cmdtab->m);
1✔
445
      }
446
    }
447
  }
448

449
  if (m->authtable) {
4✔
450
    authtable *authtab;
451

452
    for (authtab = m->authtable; authtab->name; authtab++) {
2✔
453
      pr_stash_remove_symbol(PR_SYM_AUTH, authtab->name, authtab->m);
1✔
454
    }
455
  }
456

457
  /* Remove any callbacks that the module may have registered, i.e.:
458
   *
459
   * ctrls
460
   * events
461
   * timers
462
   *
463
   * Ideally we would also automatically unregister other callbacks that
464
   * the module may have registered, such as FSIO, NetIO, variables, and
465
   * response handlers.  However, these APIs do not yet allow for
466
   * removal of all callbacks for a given module.
467
   */
468

469
#ifdef PR_USE_CTRLS
470
  pr_ctrls_unregister(m, NULL);
4✔
471
#endif /* PR_USE_CTRLS */
472
  pr_event_unregister(m, NULL, NULL);
4✔
473
  pr_timer_remove(-1, m);
4✔
474

475
  return 0;
4✔
476
}
477

478
int modules_init(void) {
25✔
479
  register unsigned int i = 0;
25✔
480

481
  for (i = 0; static_modules[i]; i++) {
25✔
482
    module *m = static_modules[i];
×
483

484
    if (pr_module_load(m) < 0) {
×
485
      pr_log_pri(PR_LOG_WARNING, "fatal: unable to load module 'mod_%s.c': %s",
×
486
        m->name, strerror(errno));
×
487
      exit(1);
×
488
    }
489
  }
490

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

© 2026 Coveralls, Inc