/
common.c
186 lines (156 loc) · 4.02 KB
/
common.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/*
* Copyright (C) 2010-2019 Red Hat, Inc. All rights reserved.
*
* Author: Fabio M. Di Nitto <fabbione@kronosnet.org>
*
* This software licensed under LGPL-2.0+
*/
#include "config.h"
#include <unistd.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <errno.h>
#include <libgen.h>
#include <link.h>
#include <string.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include "logging.h"
#include "common.h"
int _fdset_cloexec(int fd)
{
int fdflags;
fdflags = fcntl(fd, F_GETFD, 0);
if (fdflags < 0)
return -1;
fdflags |= FD_CLOEXEC;
if (fcntl(fd, F_SETFD, fdflags) < 0)
return -1;
return 0;
}
int _fdset_nonblock(int fd)
{
int fdflags;
fdflags = fcntl(fd, F_GETFL, 0);
if (fdflags < 0)
return -1;
fdflags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, fdflags) < 0)
return -1;
return 0;
}
static int get_lib_dir(void *lib_handle, char dir[MAXPATHLEN])
{
int res;
#ifndef HAVE_RTLD_DI_ORIGIN
struct link_map *lm;
char l_name[MAXPATHLEN];
#endif
#ifdef HAVE_RTLD_DI_ORIGIN
res = dlinfo(lib_handle, RTLD_DI_ORIGIN, dir);
#else
/*
* musl libc doesn't support RTLD_DI_ORIGIN
*/
res = dlinfo(lib_handle, RTLD_DI_LINKMAP, &lm);
if (res == 0) {
snprintf(l_name, sizeof(l_name), "%s", lm->l_name);
snprintf(dir, MAXPATHLEN, "%s", dirname(l_name));
}
#endif
return res;
}
static void *open_lib(knet_handle_t knet_h, const char *libname, int extra_flags)
{
void *ret = NULL;
char *error = NULL;
char dir[MAXPATHLEN], path[MAXPATHLEN * 2], link[MAXPATHLEN];
struct stat sb;
/*
* clear any pending error
*/
dlerror();
ret = dlopen(libname, RTLD_NOW | RTLD_GLOBAL | extra_flags);
if (!ret) {
error = dlerror();
if (error) {
log_err(knet_h, KNET_SUB_COMMON, "unable to dlopen %s: %s", libname, error);
} else {
log_err(knet_h, KNET_SUB_COMMON, "unable to dlopen %s: unknown error", libname);
}
errno = EAGAIN;
return NULL;
}
memset(dir, 0, sizeof(dir));
memset(link, 0, sizeof(link));
memset(path, 0, sizeof(path));
if (get_lib_dir(ret, dir) < 0) {
/*
* should we dlclose and return error?
*/
error = dlerror();
log_warn(knet_h, KNET_SUB_COMMON, "unable to dlinfo %s: %s",
libname, error);
} else {
snprintf(path, sizeof(path), "%s/%s", dir, libname);
log_info(knet_h, KNET_SUB_COMMON, "%s has been loaded from %s", libname, path);
/*
* try to resolve the library and check if it is a symlink and to where.
* we can't prevent symlink attacks but at least we can log where the library
* has been loaded from
*/
if (lstat(path, &sb) < 0) {
log_debug(knet_h, KNET_SUB_COMMON, "Unable to stat %s: %s", path, strerror(errno));
goto out;
}
if (S_ISLNK(sb.st_mode)) {
if (readlink(path, link, sizeof(link)-1) < 0) {
log_debug(knet_h, KNET_SUB_COMMON, "Unable to readlink %s: %s", path, strerror(errno));
goto out;
}
link[sizeof(link) - 1] = 0;
/*
* symlink is relative to the directory
*/
if (link[0] != '/') {
snprintf(path, sizeof(path), "%s/%s", dir, link);
log_info(knet_h, KNET_SUB_COMMON, "%s/%s is a symlink to %s", dir, libname, path);
} else {
log_info(knet_h, KNET_SUB_COMMON, "%s/%s is a symlink to %s", dir, libname, link);
}
}
}
out:
return ret;
}
void *load_module(knet_handle_t knet_h, const char *type, const char *name)
{
void *module, *ops;
log_msg_t **log_msg_sym;
char soname[MAXPATHLEN], opsname[MAXPATHLEN];
snprintf (soname, sizeof soname, "%s_%s.so", type, name);
module = open_lib(knet_h, soname, 0);
if (!module) {
return NULL;
}
log_msg_sym = dlsym (module, "log_msg");
if (!log_msg_sym) {
log_err (knet_h, KNET_SUB_COMMON, "unable to map symbol 'log_msg' in module %s: %s",
soname, dlerror ());
errno = EINVAL;
return NULL;
}
*log_msg_sym = log_msg;
snprintf (opsname, sizeof opsname, "%s_model", type);
ops = dlsym (module, opsname);
if (!ops) {
log_err (knet_h, KNET_SUB_COMMON, "unable to map symbol 'model' in module %s: %s",
soname, dlerror ());
errno = EINVAL;
return NULL;
}
return ops;
}