1 /*
2 * Copyright (C) Tildeslash Ltd. All rights reserved.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU Affero General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *
15 * In addition, as a special exception, the copyright holders give
16 * permission to link the code of portions of this program with the
17 * OpenSSL library under certain conditions as described in each
18 * individual source file, and distribute linked combinations
19 * including the two.
20 *
21 * You must obey the GNU Affero General Public License in all respects
22 * for all of the code used other than OpenSSL.
23 */
24
25 /**
26 * System dependent filesystem methods.
27 *
28 * @file
29 */
30
31 #include "config.h"
32
33 #ifdef HAVE_STDIO_H
34 #include <stdio.h>
35 #endif
36
37 #ifdef HAVE_ERRNO_H
38 #include <errno.h>
39 #endif
40
41 #ifdef HAVE_STRING_H
42 #include <string.h>
43 #endif
44
45 #ifdef HAVE_SYS_PARAM_H
46 #include <sys/param.h>
47 #endif
48
49 #ifdef HAVE_CTYPE_H
50 #include <ctype.h>
51 #endif
52
53 #ifdef HAVE_SYS_SYSCTL_H
54 #include <sys/sysctl.h>
55 #endif
56
57 #ifdef HAVE_SYS_MOUNT_H
58 #include <sys/mount.h>
59 #endif
60
61 #ifdef HAVE_SYS_TYPES_H
62 #include <sys/types.h>
63 #endif
64
65 #ifdef HAVE_SYS_STATVFS_H
66 #include <sys/statvfs.h>
67 #endif
68
69 #ifdef HAVE_SYS_IOSTAT_H
70 #include <sys/iostat.h>
71 #endif
72
73
74 #include "monit.h"
75 #include "device.h"
76
77 // libmonit
78 #include "system/Time.h"
79 #include "io/File.h"
80
81
82 /* ------------------------------------------------------------- Definitions */
83
84
85 static struct {
86 unsigned long long timestamp;
87 size_t diskCount;
88 size_t diskLength;
89 struct io_sysctl *disk;
90 } _statistics = {};
91
92
93 /* ------------------------------------------------------- Static destructor */
94
95
_destructor()96 static void __attribute__ ((destructor)) _destructor() {
97 FREE(_statistics.disk);
98 }
99
100
101 /* ----------------------------------------------------------------- Private */
102
103
104 // Parse the device path like /dev/sd0a -> sd0
_parseDevice(const char * path,Device_T device)105 static bool _parseDevice(const char *path, Device_T device) {
106 const unsigned char *base = File_basename(path);
107 for (int len = strlen(base), i = len - 1; i >= 0; i--) {
108 if (isdigit(*(base + i))) {
109 unsigned index = i + 1;
110 strncpy(device->key, base, index < sizeof(device->key) ? index : sizeof(device->key) - 1);
111 return true;
112 }
113 }
114 return false;
115 }
116
117
_getStatistics(unsigned long long now)118 static bool _getStatistics(unsigned long long now) {
119 // Refresh only if the statistics are older then 1 second (handle also backward time jumps)
120 if (now > _statistics.timestamp + 1000 || now < _statistics.timestamp - 1000) {
121 size_t len = 0;
122 int mib[3] = {CTL_HW, HW_IOSTATS, sizeof(struct io_sysctl)};
123 if (sysctl(mib, 3, NULL, &len, NULL, 0) == -1) {
124 Log_error("filesystem statistic error -- cannot get HW_IOSTATS size: %s\n", STRERROR);
125 return false;
126 }
127 if (_statistics.diskLength != len) {
128 _statistics.diskCount = len / mib[2];
129 _statistics.diskLength = len;
130 RESIZE(_statistics.disk, len);
131 }
132 if (sysctl(mib, 3, _statistics.disk, &(_statistics.diskLength), NULL, 0) == -1) {
133 Log_error("filesystem statistic error -- cannot get HW_IOSTATS: %s\n", STRERROR);
134 return false;
135 }
136 _statistics.timestamp = now;
137 }
138 return true;
139 }
140
141
_getDummyDiskActivity(void * _inf)142 static bool _getDummyDiskActivity(__attribute__ ((unused)) void *_inf) {
143 return true;
144 }
145
146
_getBlockDiskActivity(void * _inf)147 static bool _getBlockDiskActivity(void *_inf) {
148 Info_T inf = _inf;
149 unsigned long long now = Time_milli();
150 bool rv = _getStatistics(now);
151 if (rv) {
152 for (unsigned i = 0; i < _statistics.diskCount; i++) {
153 if (Str_isEqual(inf->filesystem->object.key, _statistics.disk[i].name)) {
154 Statistics_update(&(inf->filesystem->read.bytes), now, _statistics.disk[i].rbytes);
155 Statistics_update(&(inf->filesystem->write.bytes), now, _statistics.disk[i].wbytes);
156 Statistics_update(&(inf->filesystem->read.operations), now, _statistics.disk[i].rxfer);
157 Statistics_update(&(inf->filesystem->write.operations), now, _statistics.disk[i].wxfer);
158 Statistics_update(&(inf->filesystem->time.run), now, _statistics.disk[i].time_sec * 1000. + _statistics.disk[i].time_usec / 1000.);
159 break;
160 }
161 }
162 }
163 return rv;
164 }
165
166
_getDiskUsage(void * _inf)167 static bool _getDiskUsage(void *_inf) {
168 Info_T inf = _inf;
169 struct statvfs usage;
170 if (statvfs(inf->filesystem->object.mountpoint, &usage) != 0) {
171 Log_error("Error getting usage statistics for filesystem '%s' -- %s\n", inf->filesystem->object.mountpoint, STRERROR);
172 return false;
173 }
174 inf->filesystem->f_bsize = usage.f_frsize;
175 inf->filesystem->f_blocks = usage.f_blocks;
176 inf->filesystem->f_blocksfree = usage.f_bavail;
177 inf->filesystem->f_blocksfreetotal = usage.f_bfree;
178 inf->filesystem->f_files = usage.f_files;
179 inf->filesystem->f_filesfree = usage.f_ffree;
180 return true;
181 }
182
183
_compareMountpoint(const char * mountpoint,struct statvfs * mnt)184 static bool _compareMountpoint(const char *mountpoint, struct statvfs *mnt) {
185 return IS(mountpoint, mnt->f_mntonname);
186 }
187
188
_compareDevice(const char * device,struct statvfs * mnt)189 static bool _compareDevice(const char *device, struct statvfs *mnt) {
190 return IS(device, mnt->f_mntfromname);
191 }
192
193
_filesystemFlagsToString(Info_T inf,unsigned long long flags)194 static void _filesystemFlagsToString(Info_T inf, unsigned long long flags) {
195 struct mystable {
196 unsigned long long flag;
197 char *description;
198 } t[]= {
199 #ifdef MNT_DISCARD
200 {MNT_DISCARD, "discard"},
201 #endif
202 {MNT_RDONLY, "ro"},
203 {MNT_SYNCHRONOUS, "synchronous"},
204 {MNT_NOEXEC, "noexec"},
205 {MNT_NOSUID, "nosuid"},
206 {MNT_NODEV, "nodev"},
207 {MNT_NODEVMTIME, "nodevmtime"},
208 {MNT_EXTATTR, "extattr"},
209 {MNT_IGNORE, "hidden"},
210 {MNT_LOG, "log"},
211 {MNT_RELATIME, "relatime"},
212 {MNT_NOCOREDUMP, "nocoredump"},
213 {MNT_ASYNC, "asynchronous"},
214 {MNT_NOATIME, "noatime"},
215 {MNT_EXRDONLY, "exported read only"},
216 {MNT_EXPORTED, "exported"},
217 {MNT_DEFEXPORTED, "exported to the world"},
218 {MNT_EXPORTANON, "anon uid mapping"},
219 {MNT_EXKERB, "exported with kerberos"},
220 {MNT_EXPUBLIC, "public export"},
221 {MNT_EXNORESPORT, "no reserved ports enforcement"},
222 {MNT_LOCAL, "local"},
223 {MNT_QUOTA, "quota"},
224 {MNT_ROOTFS, "rootfs"},
225 {MNT_SOFTDEP, "soft dependencies"},
226 {MNT_SYMPERM, "symperm"},
227 {MNT_UNION, "union"}
228 };
229 Util_swapFilesystemFlags(&(inf->filesystem->flags));
230 for (unsigned i = 0, count = 0; i < sizeof(t) / sizeof(t[0]); i++) {
231 if (flags & t[i].flag) {
232 snprintf(inf->filesystem->flags.current + strlen(inf->filesystem->flags.current), sizeof(inf->filesystem->flags.value[0]) - strlen(inf->filesystem->flags.current) - 1, "%s%s", count++ ? ", " : "", t[i].description);
233 }
234 }
235 }
236
237
_setDevice(Info_T inf,const char * path,bool (* compare)(const char * path,struct statvfs * mnt))238 static bool _setDevice(Info_T inf, const char *path, bool (*compare)(const char *path, struct statvfs *mnt)) {
239 int countfs = getvfsstat(NULL, 0, MNT_NOWAIT);
240 if (countfs != -1) {
241 struct statvfs *mnt = CALLOC(countfs, sizeof(struct statvfs));
242 if ((countfs = getvfsstat(mnt, countfs * sizeof(struct statvfs), MNT_NOWAIT)) != -1) {
243 for (int i = 0; i < countfs; i++) {
244 struct statvfs *mntItem = mnt + i;
245 if (compare(path, mntItem)) {
246 if (IS(mntItem->f_fstypename, "ffs")) {
247 if (_parseDevice(mntItem->f_mntfromname, &(inf->filesystem->object))) {
248 inf->filesystem->object.getDiskActivity = _getBlockDiskActivity;
249 } else {
250 inf->filesystem->object.getDiskActivity = _getDummyDiskActivity;
251 DEBUG("I/O monitoring for filesystem '%s' skipped - unable to parse the device %s", path, mntItem->f_mntfromname);
252 }
253 } else {
254 //FIXME: NetBSD kernel has NFS statistics as well, but there is no clear mapping between the kernel label ("nfsX" style) and the NFS mount => we don't support NFS currently
255 inf->filesystem->object.getDiskActivity = _getDummyDiskActivity;
256 }
257 inf->filesystem->object.flags = mntItem->f_flag & MNT_VISFLAGMASK;
258 _filesystemFlagsToString(inf, inf->filesystem->object.flags);
259 strncpy(inf->filesystem->object.device, mntItem->f_mntfromname, sizeof(inf->filesystem->object.device) - 1);
260 strncpy(inf->filesystem->object.mountpoint, mntItem->f_mntonname, sizeof(inf->filesystem->object.mountpoint) - 1);
261 strncpy(inf->filesystem->object.type, mntItem->f_fstypename, sizeof(inf->filesystem->object.type) - 1);
262 inf->filesystem->object.getDiskUsage = _getDiskUsage;
263 inf->filesystem->object.mounted = true;
264 FREE(mnt);
265 return true;
266 }
267 }
268 }
269 FREE(mnt);
270 }
271 Log_error("Lookup for '%s' filesystem failed\n", path);
272 error:
273 inf->filesystem->object.mounted = false;
274 return false;
275 }
276
277
_getDevice(Info_T inf,const char * path,bool (* compare)(const char * path,struct statvfs * mnt))278 static bool _getDevice(Info_T inf, const char *path, bool (*compare)(const char *path, struct statvfs *mnt)) {
279 if (_setDevice(inf, path, compare)) {
280 return (inf->filesystem->object.getDiskUsage(inf) && inf->filesystem->object.getDiskActivity(inf));
281 }
282 return false;
283 }
284
285
286 /* ------------------------------------------------------------------ Public */
287
288
Filesystem_getByMountpoint(Info_T inf,const char * path)289 bool Filesystem_getByMountpoint(Info_T inf, const char *path) {
290 ASSERT(inf);
291 ASSERT(path);
292 return _getDevice(inf, path, _compareMountpoint);
293 }
294
295
Filesystem_getByDevice(Info_T inf,const char * path)296 bool Filesystem_getByDevice(Info_T inf, const char *path) {
297 ASSERT(inf);
298 ASSERT(path);
299 return _getDevice(inf, path, _compareDevice);
300 }
301
302