Skip to content

Commit 129616c

Browse files
committed
MDEV-28592 disks plugin - getmntinfo (BSD) & getmntent (AIX)
Thanks to references from Brad Smith, BSDs use getmntinfo as a system call for mounted filesystems. Most BSDs return statfs structures, (and we use OSX's statfs64), but NetBSD uses a statvfs structure. Simplify Linux getmntent_r to just use getmntent. AIX uses getmntent. An attempt at writing Solaris compatibility with a small bit of HPUX compatibility was made based on man page entries only. Fixes welcome. statvfs structures now use f_bsize for consistency with statfs Test case adjusted as PATH_MAX is OS defined (e.g. 1023 on AIX) Fixes: 0ee5cf8 also fixes: MDEV-27818: Disk plugin does not show zpool mounted devices This is because zpool mounted point don't begin with /. Due to the proliferation of multiple filesystem types since this was written, we restrict the entries listed in the disks plugin to excude: * read only mount points (no point monitoring, and includes squash, snaps, sysfs, procfs, cgroups...) * mount points that aren't directories (excludes /etc/hostname and similar mounts in containers). (getmntent (Linux/AIX) only) * exclude systems where there is no capacity listed (excludes various virtual filesystem types). Reviewer: Sergei Golubchik
1 parent 57739ae commit 129616c

File tree

6 files changed

+211
-55
lines changed

6 files changed

+211
-55
lines changed

cmake/os/WindowsCache.cmake

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ SET(HAVE_GETHOSTBYADDR_R CACHE INTERNAL "")
6464
SET(HAVE_GETHRTIME CACHE INTERNAL "")
6565
SET(HAVE_GETPAGESIZE CACHE INTERNAL "")
6666
SET(HAVE_GETPASS CACHE INTERNAL "")
67+
SET(HAVE_GETMNTENT CACHE INTERNAL "")
68+
SET(HAVE_GETMNTENT_IN_SYS_MNTAB CACHE INTERNAL "")
69+
SET(HAVE_GETMNTINFO CACHE INTERNAL "")
70+
SET(HAVE_GETMNTINFO64 CACHE INTERNAL "")
6771
SET(HAVE_GETPASSPHRASE CACHE INTERNAL "")
6872
SET(HAVE_GETPWNAM CACHE INTERNAL "")
6973
SET(HAVE_GETPWUID CACHE INTERNAL "")
@@ -146,6 +150,7 @@ SET(HAVE_SELECT 1 CACHE INTERNAL "")
146150
SET(HAVE_SELECT_H CACHE INTERNAL "")
147151
SET(HAVE_SETENV CACHE INTERNAL "")
148152
SET(HAVE_SETLOCALE 1 CACHE INTERNAL "")
153+
SET(HAVE_SETMNTENT CACHE INTERNAL "")
149154
SET(HAVE_SIGACTION CACHE INTERNAL "")
150155
SET(HAVE_SIGINT 1 CACHE INTERNAL "")
151156
SET(HAVE_SIGPIPE CACHE INTERNAL "")

config.h.cmake

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@
3535
#cmakedefine HAVE_FLOAT_H 1
3636
#cmakedefine HAVE_FNMATCH_H 1
3737
#cmakedefine HAVE_FPU_CONTROL_H 1
38+
#cmakedefine HAVE_GETMNTENT 1
39+
#cmakedefine HAVE_GETMNTENT_IN_SYS_MNTAB 1
40+
#cmakedefine HAVE_GETMNTINFO 1
41+
#cmakedefine HAVE_GETMNTINFO64 1
42+
#cmakedefine HAVE_GETMNTINFO_TAKES_statvfs 1
3843
#cmakedefine HAVE_GRP_H 1
3944
#cmakedefine HAVE_IA64INTRIN_H 1
4045
#cmakedefine HAVE_IEEEFP_H 1
@@ -214,6 +219,7 @@
214219
#cmakedefine HAVE_SELECT 1
215220
#cmakedefine HAVE_SETENV 1
216221
#cmakedefine HAVE_SETLOCALE 1
222+
#cmakedefine HAVE_SETMNTENT 1
217223
#cmakedefine HAVE_SETUPTERM 1
218224
#cmakedefine HAVE_SIGSET 1
219225
#cmakedefine HAVE_SIGACTION 1

plugin/disks/CMakeLists.txt

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,24 @@
11
INCLUDE (CheckIncludeFiles)
2-
CHECK_INCLUDE_FILES ("sys/statvfs.h;mntent.h" INFO_HEADERS LANGUAGE CXX)
32

4-
IF (INFO_HEADERS)
3+
CHECK_SYMBOL_EXISTS (getmntent "mntent.h" HAVE_GETMNTENT)
4+
CHECK_SYMBOL_EXISTS (getmntent "sys/mnttab.h" HAVE_GETMNTENT_IN_SYS_MNTAB)
5+
CHECK_SYMBOL_EXISTS (setmntent "mntent.h" HAVE_SETMNTENT)
6+
CHECK_SYMBOL_EXISTS (getmntinfo "sys/types.h;sys/mount.h" HAVE_GETMNTINFO)
7+
CHECK_SYMBOL_EXISTS (getmntinfo64 "sys/types.h;sys/mount.h" HAVE_GETMNTINFO64)
8+
9+
IF (HAVE_GETMNTINFO)
10+
CHECK_CXX_SOURCE_COMPILES("
11+
#include <sys/types.h>
12+
#include <sys/statvfs.h>
13+
int main()
14+
{
15+
struct statvfs *s;
16+
return getmntinfo(&s, ST_WAIT);
17+
}
18+
" HAVE_GETMNTINFO_TAKES_statvfs)
19+
ENDIF()
20+
IF (HAVE_GETMNTENT OR HAVE_GETMNTENT_IN_SYS_MNTAB OR
21+
HAVE_GETMNTINFO OR HAVE_GETMNTINFO64)
522
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/sql)
623
MYSQL_ADD_PLUGIN(DISKS information_schema_disks.cc MODULE_ONLY RECOMPILE_FOR_EMBEDDED)
724
ENDIF()

plugin/disks/information_schema_disks.cc

Lines changed: 178 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,45 @@
1717
#include <my_global.h>
1818
#include <sys/statvfs.h>
1919
#include <sys/types.h>
20+
#if defined(HAVE_GETMNTENT)
2021
#include <mntent.h>
22+
#elif !defined(HAVE_GETMNTINFO_TAKES_statvfs)
23+
/* getmntinfo (the not NetBSD variants) */
24+
#include <sys/param.h>
25+
#include <sys/ucred.h>
26+
#include <sys/mount.h>
27+
#endif
28+
#if defined(HAVE_GETMNTENT_IN_SYS_MNTAB)
29+
#include <sys/mnttab.h>
30+
#define HAVE_GETMNTENT
31+
#endif
2132
#include <sql_class.h>
2233
#include <table.h>
2334
#include <sql_acl.h> /* check_global_access() */
2435

36+
/*
37+
This intends to support *BSD's, macOS, Solaris, AIX, HP-UX, and Linux.
38+
39+
specificly:
40+
FreeBSD/OpenBSD/DragonFly (statfs) NetBSD (statvfs) uses getmntinfo().
41+
macOS uses getmntinfo64().
42+
Linux can use getmntent_r(), but we've just used getmntent for simplification.
43+
Linux/Solaris/AIX/HP-UX uses setmntent()/getmntent().
44+
Solaris uses getmntent() with a diffent prototype, return structure, and
45+
no setmntent(fopen instead)
46+
*/
47+
#if defined(HAVE_GETMNTINFO_TAKES_statvfs) || defined(HAVE_GETMNTENT)
48+
typedef struct statvfs st_info;
49+
#elif defined(HAVE_GETMNTINFO64)
50+
typedef struct statfs64 st_info;
51+
#else // GETMNTINFO
52+
typedef struct statfs st_info;
53+
#endif
54+
#ifndef MOUNTED
55+
/* HPUX - https://docstore.mik.ua/manuals/hp-ux/en/B2355-60130/getmntent.3X.html */
56+
#define MOUNTED MNT_MNTTAB
57+
#endif
58+
2559
bool schema_table_store_record(THD *thd, TABLE *table);
2660

2761
namespace
@@ -39,23 +73,40 @@ ST_FIELD_INFO disks_table_fields[]=
3973
{ 0, 0, MYSQL_TYPE_NULL, 0, 0, 0, 0 }
4074
};
4175

42-
int disks_table_add_row(THD* pThd,
43-
TABLE* pTable,
44-
const char* zDisk,
45-
const char* zPath,
46-
const struct statvfs& info)
76+
static int disks_table_add_row_stat(
77+
THD* pThd,
78+
TABLE* pTable,
79+
const char* zDisk,
80+
const char* zPath,
81+
const st_info &info)
4782
{
4883
// From: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/sys/statvfs.h.html
84+
// and same for statfs:
85+
// From: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/statfs.2.html#//apple_ref/doc/man/2/statfs
86+
// and: https://www.freebsd.org/cgi/man.cgi?query=statfs&sektion=2&apropos=0&manpath=FreeBSD+13.1-RELEASE+and+Ports
4987
//
50-
// f_frsize Fundamental file system block size.
88+
// f_bsize Fundamental file system block size.
5189
// f_blocks Total number of blocks on file system in units of f_frsize.
5290
// f_bfree Total number of free blocks.
5391
// f_bavail Number of free blocks available to non-privileged process.
92+
ulong block_size= (ulong) info.f_bsize;
5493

55-
ulonglong total = ((ulonglong)info.f_frsize * info.f_blocks) / 1024;
56-
ulonglong used = ((ulonglong)info.f_frsize *
94+
ulonglong total = ((ulonglong) block_size * info.f_blocks) / 1024;
95+
ulonglong used = ((ulonglong) block_size *
5796
(info.f_blocks - info.f_bfree)) / 1024;
58-
ulonglong avail = ((ulonglong)info.f_frsize * info.f_bavail) / 1024;
97+
ulonglong avail = ((ulonglong) block_size * info.f_bavail) / 1024;
98+
99+
/* skip filesystems that don't have any space */
100+
if (!info.f_blocks)
101+
return 0;
102+
103+
/* skip RO mounted filesystems */
104+
#if defined(HAVE_GETMNTINFO_TAKES_statvfs) || defined(HAVE_GETMNTENT)
105+
if (info.f_flag & ST_RDONLY)
106+
#else
107+
if (info.f_flags & MNT_RDONLY)
108+
#endif
109+
return 0;
59110

60111
pTable->field[0]->store(zDisk, strlen(zDisk), system_charset_info);
61112
pTable->field[1]->store(zPath, strlen(zPath), system_charset_info);
@@ -67,71 +118,147 @@ int disks_table_add_row(THD* pThd,
67118
return (schema_table_store_record(pThd, pTable) != 0) ? 1 : 0;
68119
}
69120

70-
int disks_table_add_row(THD* pThd, TABLE* pTable, const char* zDisk, const char* zPath)
121+
122+
#ifdef HAVE_GETMNTENT
123+
static int disks_table_add_row(THD* pThd, TABLE* pTable, const char* zDisk, const char* zPath)
71124
{
72125
int rv = 0;
73126

74-
struct statvfs info;
127+
st_info info;
75128

76129
if (statvfs(zPath, &info) == 0) // We ignore failures.
77130
{
78-
rv = disks_table_add_row(pThd, pTable, zDisk, zPath, info);
131+
rv = disks_table_add_row_stat(pThd, pTable, zDisk, zPath, info);
79132
}
80133

81134
return rv;
82135
}
136+
#endif
83137

84-
int disks_fill_table(THD* pThd, TABLE_LIST* pTables, Item* pCond)
138+
139+
#ifdef HAVE_GETMNTINFO
140+
static int disks_fill_table(THD* pThd, TABLE_LIST* pTables, Item* pCond)
85141
{
86-
int rv = 1;
87-
TABLE* pTable = pTables->table;
142+
st_info *s;
143+
int count, rv= 0;
144+
TABLE* pTable= pTables->table;
88145

89146
if (check_global_access(pThd, FILE_ACL, true))
90-
return 0;
147+
return 0;
148+
149+
#if defined(HAVE_GETMNTINFO_TAKES_statvfs)
150+
count= getmntinfo(&s, ST_WAIT);
151+
#elif defined(HAVE_GETMNTINFO64)
152+
count= getmntinfo64(&s, MNT_WAIT);
153+
#else
154+
count= getmntinfo(&s, MNT_WAIT);
155+
#endif
156+
if (count == 0)
157+
return 1;
158+
159+
while (count && rv == 0)
160+
{
161+
rv= disks_table_add_row_stat(pThd, pTable, s->f_mntfromname, s->f_mntonname, *s);
162+
count--;
163+
s++;
164+
}
165+
return rv;
166+
}
167+
#else /* HAVE_GETMNTINFO */
168+
169+
static mysql_mutex_t m_getmntent;
91170

92-
FILE* pFile = setmntent("/etc/mtab", "r");
171+
/* HAVE_GETMNTENT */
172+
static int disks_fill_table(THD* pThd, TABLE_LIST* pTables, Item* pCond)
173+
{
174+
int rv= 1;
175+
#ifdef HAVE_SETMNTENT
176+
struct mntent* pEnt;
177+
#else
178+
struct mnttab mnttabent, *pEnt= &mnttabent;
179+
#endif
180+
FILE* pFile;
181+
TABLE* pTable= pTables->table;
93182

94-
if (pFile)
183+
if (check_global_access(pThd, FILE_ACL, true))
184+
return 0;
185+
186+
#ifdef HAVE_SETMNTENT
187+
pFile= setmntent(MOUNTED, "r");
188+
#else
189+
/* Solaris */
190+
pFile= fopen("/etc/mnttab", "r");
191+
#endif
192+
193+
if (!pFile)
194+
return 1;
195+
196+
rv= 0;
197+
198+
/*
199+
We lock the outer loop rather than between getmntent so the multiple
200+
infomation_schema.disks reads don't all start blocking each other and
201+
no-one gets any answers.
202+
*/
203+
mysql_mutex_lock(&m_getmntent);
204+
205+
while ((rv == 0) &&
206+
#if defined(HAVE_SETMNTENT)
207+
(pEnt = getmntent(pFile))
208+
209+
#else
210+
getmntent(pFile, pEnt) != 0
211+
#endif
212+
)
95213
{
96-
const size_t BUFFER_SIZE = 4096; // 4K should be sufficient.
97-
98-
char* pBuffer = new (std::nothrow) char [BUFFER_SIZE];
99-
100-
if (pBuffer)
101-
{
102-
rv = 0;
103-
104-
struct mntent ent;
105-
struct mntent* pEnt;
106-
107-
while ((rv == 0) && (pEnt = getmntent_r(pFile, &ent, pBuffer, BUFFER_SIZE)))
108-
{
109-
// We only report the ones that refer to physical disks.
110-
if (pEnt->mnt_fsname[0] == '/')
111-
{
112-
rv = disks_table_add_row(pThd, pTable, pEnt->mnt_fsname, pEnt->mnt_dir);
113-
}
114-
}
115-
116-
delete [] pBuffer;
117-
}
118-
else
119-
{
120-
rv = 1;
121-
}
122-
123-
endmntent(pFile);
214+
struct stat f;
215+
const char *path, *point;
216+
#ifdef HAVE_SETMNTENT
217+
path= pEnt->mnt_dir;
218+
point= pEnt->mnt_fsname;
219+
#else
220+
path= pEnt->mnt_mountp;
221+
point= pEnt->mnt_special;
222+
#endif
223+
// Try to keep to real storage by excluding
224+
// read only mounts, and mount points that aren't directories
225+
if (hasmntopt(pEnt, MNTOPT_RO) != NULL)
226+
continue;
227+
if (stat(path, &f))
228+
continue;
229+
if (!S_ISDIR(f.st_mode))
230+
continue;
231+
rv= disks_table_add_row(pThd, pTable, point, path);
124232
}
233+
mysql_mutex_unlock(&m_getmntent);
234+
235+
#ifdef HAVE_SETMNTENT
236+
endmntent(pFile);
237+
#else
238+
fclose(pFile);
239+
#endif
125240

126241
return rv;
127242
}
243+
#endif /* HAVE_GETMNTINFO */
128244

129-
int disks_table_init(void *ptr)
245+
static int disks_table_init(void *ptr)
130246
{
131247
ST_SCHEMA_TABLE* pSchema_table = (ST_SCHEMA_TABLE*)ptr;
132248

133249
pSchema_table->fields_info = disks_table_fields;
134250
pSchema_table->fill_table = disks_fill_table;
251+
#ifndef HAVE_GETMNTINFO
252+
mysql_mutex_init(0, &m_getmntent, MY_MUTEX_INIT_SLOW);
253+
#endif
254+
return 0;
255+
}
256+
257+
static int disks_table_deinit(void *ptr __attribute__((unused)))
258+
{
259+
#ifndef HAVE_GETMNTINFO
260+
mysql_mutex_destroy(&m_getmntent);
261+
#endif
135262
return 0;
136263
}
137264

@@ -145,15 +272,15 @@ maria_declare_plugin(disks)
145272
MYSQL_INFORMATION_SCHEMA_PLUGIN,
146273
&disks_table_info, /* type-specific descriptor */
147274
"DISKS", /* table name */
148-
"Johan Wikman", /* author */
275+
"Johan Wikman, Daniel Black", /* author */
149276
"Disk space information", /* description */
150277
PLUGIN_LICENSE_GPL, /* license type */
151278
disks_table_init, /* init function */
152-
NULL, /* deinit function */
153-
0x0101, /* version = 1.1 */
279+
disks_table_deinit, /* deinit function */
280+
0x0102, /* version = 1.2 */
154281
NULL, /* no status variables */
155282
NULL, /* no system variables */
156-
"1.1", /* String version representation */
283+
"1.2", /* String version representation */
157284
MariaDB_PLUGIN_MATURITY_STABLE /* Maturity (see include/mysql/plugin.h)*/
158285
}
159286
mysql_declare_plugin_end;

plugin/disks/mysql-test/disks/disks.result

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
show create table information_schema.disks;
22
Table Create Table
33
DISKS CREATE TEMPORARY TABLE `DISKS` (
4-
`Disk` varchar(4096) NOT NULL,
5-
`Path` varchar(4096) NOT NULL,
4+
`Disk` varchar(pathlen) NOT NULL,
5+
`Path` varchar(pathlen) NOT NULL,
66
`Total` bigint(32) NOT NULL,
77
`Used` bigint(32) NOT NULL,
88
`Available` bigint(32) NOT NULL
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
--replace_regex /varchar\([0-9]+\)/varchar(pathlen)/
12
show create table information_schema.disks;
23
select sum(Total) > sum(Available), sum(Total)>sum(Used) from information_schema.disks;

0 commit comments

Comments
 (0)