diff options
Diffstat (limited to 'plugin/disks')
-rw-r--r-- | plugin/disks/CMakeLists.txt | 24 | ||||
-rw-r--r-- | plugin/disks/README.txt | 86 | ||||
-rw-r--r-- | plugin/disks/information_schema_disks.cc | 290 | ||||
-rw-r--r-- | plugin/disks/mysql-test/disks/disks.result | 12 | ||||
-rw-r--r-- | plugin/disks/mysql-test/disks/disks.test | 3 | ||||
-rw-r--r-- | plugin/disks/mysql-test/disks/disks_notembedded.result | 22 | ||||
-rw-r--r-- | plugin/disks/mysql-test/disks/disks_notembedded.test | 25 | ||||
-rw-r--r-- | plugin/disks/mysql-test/disks/suite.opt | 1 | ||||
-rw-r--r-- | plugin/disks/mysql-test/disks/suite.pm | 10 |
9 files changed, 473 insertions, 0 deletions
diff --git a/plugin/disks/CMakeLists.txt b/plugin/disks/CMakeLists.txt new file mode 100644 index 00000000..4e40842c --- /dev/null +++ b/plugin/disks/CMakeLists.txt @@ -0,0 +1,24 @@ +INCLUDE (CheckIncludeFiles) + +CHECK_SYMBOL_EXISTS (getmntent "mntent.h" HAVE_GETMNTENT) +CHECK_SYMBOL_EXISTS (getmntent "sys/mnttab.h" HAVE_GETMNTENT_IN_SYS_MNTAB) +CHECK_SYMBOL_EXISTS (setmntent "mntent.h" HAVE_SETMNTENT) +CHECK_SYMBOL_EXISTS (getmntinfo "sys/types.h;sys/mount.h" HAVE_GETMNTINFO) +CHECK_SYMBOL_EXISTS (getmntinfo64 "sys/types.h;sys/mount.h" HAVE_GETMNTINFO64) + +IF (HAVE_GETMNTINFO) +CHECK_CXX_SOURCE_COMPILES(" +#include <sys/types.h> +#include <sys/statvfs.h> +int main() +{ + struct statvfs *s; + return getmntinfo(&s, ST_WAIT); +} + " HAVE_GETMNTINFO_TAKES_statvfs) +ENDIF() +IF (HAVE_GETMNTENT OR HAVE_GETMNTENT_IN_SYS_MNTAB OR + HAVE_GETMNTINFO OR HAVE_GETMNTINFO64) + INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/sql) + MYSQL_ADD_PLUGIN(DISKS information_schema_disks.cc MODULE_ONLY RECOMPILE_FOR_EMBEDDED) +ENDIF() diff --git a/plugin/disks/README.txt b/plugin/disks/README.txt new file mode 100644 index 00000000..b49db3c0 --- /dev/null +++ b/plugin/disks/README.txt @@ -0,0 +1,86 @@ +Information Schema Disks +------------------------ +This is a proof-of-concept information schema plugin that allows the +disk space situation to be monitored. When installed, it can be used +as follows: + + > select * from information_schema.disks; + +-----------+-----------------------+-----------+----------+-----------+ + | Disk | Path | Total | Used | Available | + +-----------+-----------------------+-----------+----------+-----------+ + | /dev/sda3 | / | 47929956 | 30666304 | 14805864 | + | /dev/sda1 | /boot/efi | 191551 | 3461 | 188090 | + | /dev/sda4 | /home | 174679768 | 80335392 | 85448120 | + | /dev/sdb1 | /mnt/hdd | 961301832 | 83764 | 912363644 | + | /dev/sdb1 | /home/wikman/Music | 961301832 | 83764 | 912363644 | + | /dev/sdb1 | /home/wikman/Videos | 961301832 | 83764 | 912363644 | + | /dev/sdb1 | /home/wikman/hdd | 961301832 | 83764 | 912363644 | + | /dev/sdb1 | /home/wikman/Pictures | 961301832 | 83764 | 912363644 | + | /dev/sda3 | /var/lib/docker/aufs | 47929956 | 30666304 | 14805864 | + +-----------+-----------------------+-----------+----------+-----------+ + 9 rows in set (0.00 sec) + +- 'Disk' is the name of the disk itself. +- 'Path' is the mount point of the disk. +- 'Total' is the total space in KiB. +- 'Used' is the used amount of space in KiB, and +- 'Available' is the amount of space in KiB available to non-root users. + +Note that as the amount of space available to root may be more that what +is available to non-root users, 'available' + 'used' may be less than 'total'. + +All paths to which a particular disk has been mounted are reported. The +rationale is that someone might want to take different action e.g. depending +on which disk is relevant for a particular path. This leads to the same disk +being reported multiple times. An alternative to this would be to have two +tables; disks and mounts. + + > select * from information_schema.disks; + +-----------+-----------+----------+-----------+ + | Disk | Total | Used | Available | + +-----------+-----------+----------+-----------+ + | /dev/sda3 | 47929956 | 30666304 | 14805864 | + | /dev/sda1 | 191551 | 3461 | 188090 | + | /dev/sda4 | 174679768 | 80335392 | 85448120 | + | /dev/sdb1 | 961301832 | 83764 | 912363644 | + +-----------+-----------+----------+-----------+ + + > select * from information_schema.mounts; + +-----------------------+-----------+ + | Path | Disk | + +-----------------------+-----------+ + | / | /dev/sda3 | + | /boot/efi | /dev/sda1 | + | /home | /dev/sda4 | + | /mnt/hdd | /dev/sdb1 | + | /home/wikman/Music | /dev/sdb1 | + ... + + +Installation +------------ + +- Use "install plugin" or "install soname" command: + + MariaDB [(none)]> install plugin disks soname 'disks.so'; + + or + + MariaDB [(none)]> install soname 'disks.so'; + +Usage +----- +The plugin appears as the table 'disks' in 'information_schema'. + + MariaDB [(none)]> select * from information_schema.disks; + +-----------+-----------------------+-----------+----------+-----------+ + | Disk | Path | Total | Used | Available | + +-----------+-----------------------+-----------+----------+-----------+ + | /dev/sda3 | / | 47929956 | 30666308 | 14805860 | + | /dev/sda1 | /boot/efi | 191551 | 3461 | 188090 | + | /dev/sda4 | /home | 174679768 | 80348148 | 85435364 | + | /dev/sdb1 | /mnt/hdd | 961301832 | 83764 | 912363644 | + | /dev/sdb1 | /home/wikman/Music | 961301832 | 83764 | 912363644 | + | /dev/sdb1 | /home/wikman/Videos | 961301832 | 83764 | 912363644 | + ... + diff --git a/plugin/disks/information_schema_disks.cc b/plugin/disks/information_schema_disks.cc new file mode 100644 index 00000000..15e26dad --- /dev/null +++ b/plugin/disks/information_schema_disks.cc @@ -0,0 +1,290 @@ +/* + Copyright (c) 2017, 2019, MariaDB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include <my_global.h> +#include <sys/statvfs.h> +#include <sys/types.h> +#if defined(HAVE_GETMNTENT) +#include <mntent.h> +#elif !defined(HAVE_GETMNTINFO_TAKES_statvfs) +/* getmntinfo (the not NetBSD variants) */ +#include <sys/param.h> +#include <sys/ucred.h> +#include <sys/mount.h> +#endif +#if defined(HAVE_GETMNTENT_IN_SYS_MNTAB) +#include <sys/mnttab.h> +#define HAVE_GETMNTENT +#endif +#include <sql_class.h> +#include <sql_i_s.h> +#include <sql_acl.h> /* check_global_access() */ + +/* + This intends to support *BSD's, macOS, Solaris, AIX, HP-UX, and Linux. + + specificly: + FreeBSD/OpenBSD/DragonFly (statfs) NetBSD (statvfs) uses getmntinfo(). + macOS uses getmntinfo64(). + Linux can use getmntent_r(), but we've just used getmntent for simplification. + Linux/Solaris/AIX/HP-UX uses setmntent()/getmntent(). + Solaris uses getmntent() with a diffent prototype, return structure, and + no setmntent(fopen instead) +*/ +#if defined(HAVE_GETMNTINFO_TAKES_statvfs) || defined(HAVE_GETMNTENT) +typedef struct statvfs st_info; +#elif defined(HAVE_GETMNTINFO64) +typedef struct statfs64 st_info; +#else // GETMNTINFO +typedef struct statfs st_info; +#endif +#ifndef MOUNTED +/* HPUX - https://docstore.mik.ua/manuals/hp-ux/en/B2355-60130/getmntent.3X.html */ +#define MOUNTED MNT_MNTTAB +#endif + +bool schema_table_store_record(THD *thd, TABLE *table); + + +struct st_mysql_information_schema disks_table_info = { MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION }; + + +namespace Show { + +ST_FIELD_INFO disks_table_fields[]= +{ + Column("Disk", Varchar(PATH_MAX), NOT_NULL), + Column("Path", Varchar(PATH_MAX), NOT_NULL), + Column("Total", SLonglong(32), NOT_NULL), // Total amount available + Column("Used", SLonglong(32), NOT_NULL), // Amount of space used + Column("Available", SLonglong(32), NOT_NULL), // Amount available to users other than root. + CEnd() +}; + + +static int disks_table_add_row_stat( + THD* pThd, + TABLE* pTable, + const char* zDisk, + const char* zPath, + const st_info &info) +{ + // From: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/sys/statvfs.h.html + // and same for statfs: + // From: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/statfs.2.html#//apple_ref/doc/man/2/statfs + // and: https://www.freebsd.org/cgi/man.cgi?query=statfs&sektion=2&apropos=0&manpath=FreeBSD+13.1-RELEASE+and+Ports + // + // f_bsize Fundamental file system block size. + // f_blocks Total number of blocks on file system in units of f_frsize. + // f_bfree Total number of free blocks. + // f_bavail Number of free blocks available to non-privileged process. + ulong block_size= (ulong) info.f_bsize; + + ulonglong total = ((ulonglong) block_size * info.f_blocks) / 1024; + ulonglong used = ((ulonglong) block_size * + (info.f_blocks - info.f_bfree)) / 1024; + ulonglong avail = ((ulonglong) block_size * info.f_bavail) / 1024; + + /* skip filesystems that don't have any space */ + if (!info.f_blocks) + return 0; + + /* skip RO mounted filesystems */ +#if defined(HAVE_GETMNTINFO_TAKES_statvfs) || defined(HAVE_GETMNTENT) + if (info.f_flag & ST_RDONLY) +#else + if (info.f_flags & MNT_RDONLY) +#endif + return 0; + + pTable->field[0]->store(zDisk, strlen(zDisk), system_charset_info); + pTable->field[1]->store(zPath, strlen(zPath), system_charset_info); + pTable->field[2]->store(total); + pTable->field[3]->store(used); + pTable->field[4]->store(avail); + + // 0 means success. + return (schema_table_store_record(pThd, pTable) != 0) ? 1 : 0; +} + + +#ifdef HAVE_GETMNTENT +static int disks_table_add_row(THD* pThd, TABLE* pTable, const char* zDisk, const char* zPath) +{ + int rv = 0; + + st_info info; + + if (statvfs(zPath, &info) == 0) // We ignore failures. + { + rv = disks_table_add_row_stat(pThd, pTable, zDisk, zPath, info); + } + + return rv; +} +#endif + + +#ifdef HAVE_GETMNTINFO +static int disks_fill_table(THD* pThd, TABLE_LIST* pTables, Item* pCond) +{ + st_info *s; + int count, rv= 0; + TABLE* pTable= pTables->table; + + if (check_global_access(pThd, FILE_ACL, true)) + return 0; + +#if defined(HAVE_GETMNTINFO_TAKES_statvfs) + count= getmntinfo(&s, ST_WAIT); +#elif defined(HAVE_GETMNTINFO64) + count= getmntinfo64(&s, MNT_WAIT); +#else + count= getmntinfo(&s, MNT_WAIT); +#endif + if (count == 0) + return 1; + + while (count && rv == 0) + { + rv= disks_table_add_row_stat(pThd, pTable, s->f_mntfromname, s->f_mntonname, *s); + count--; + s++; + } + return rv; +} +#else /* HAVE_GETMNTINFO */ + +static mysql_mutex_t m_getmntent; + +/* HAVE_GETMNTENT */ +static int disks_fill_table(THD* pThd, TABLE_LIST* pTables, Item* pCond) +{ + int rv= 1; +#ifdef HAVE_SETMNTENT + struct mntent* pEnt; +#else + struct mnttab mnttabent, *pEnt= &mnttabent; +#endif + FILE* pFile; + TABLE* pTable= pTables->table; + + if (check_global_access(pThd, FILE_ACL, true)) + return 0; + +#ifdef HAVE_SETMNTENT + pFile= setmntent(MOUNTED, "r"); +#else + /* Solaris */ + pFile= fopen("/etc/mnttab", "r"); +#endif + + if (!pFile) + return 1; + + rv= 0; + + /* + We lock the outer loop rather than between getmntent so the multiple + infomation_schema.disks reads don't all start blocking each other and + no-one gets any answers. + */ + mysql_mutex_lock(&m_getmntent); + + while ((rv == 0) && +#if defined(HAVE_SETMNTENT) + (pEnt = getmntent(pFile)) + +#else + getmntent(pFile, pEnt) != 0 +#endif + ) + { + struct stat f; + const char *path, *point; +#ifdef HAVE_SETMNTENT + path= pEnt->mnt_dir; + point= pEnt->mnt_fsname; +#else + path= pEnt->mnt_mountp; + point= pEnt->mnt_special; +#endif + // Try to keep to real storage by excluding + // read only mounts, and mount points that aren't directories + if (hasmntopt(pEnt, MNTOPT_RO) != NULL) + continue; + if (stat(path, &f)) + continue; + if (!S_ISDIR(f.st_mode)) + continue; + rv= disks_table_add_row(pThd, pTable, point, path); + } + mysql_mutex_unlock(&m_getmntent); + +#ifdef HAVE_SETMNTENT + endmntent(pFile); +#else + fclose(pFile); +#endif + + return rv; +} +#endif /* HAVE_GETMNTINFO */ + +static int disks_table_init(void *ptr) +{ + ST_SCHEMA_TABLE* pSchema_table = (ST_SCHEMA_TABLE*)ptr; + + pSchema_table->fields_info = disks_table_fields; + pSchema_table->fill_table = disks_fill_table; +#ifndef HAVE_GETMNTINFO + mysql_mutex_init(0, &m_getmntent, MY_MUTEX_INIT_SLOW); +#endif + return 0; +} + +static int disks_table_deinit(void *ptr __attribute__((unused))) +{ +#ifndef HAVE_GETMNTINFO + mysql_mutex_destroy(&m_getmntent); +#endif + return 0; +} + +} // namespace Show + +extern "C" +{ + +maria_declare_plugin(disks) +{ + MYSQL_INFORMATION_SCHEMA_PLUGIN, + &disks_table_info, /* type-specific descriptor */ + "DISKS", /* table name */ + "Johan Wikman, Daniel Black", /* author */ + "Disk space information", /* description */ + PLUGIN_LICENSE_GPL, /* license type */ + Show::disks_table_init, /* init function */ + Show::disks_table_deinit, /* deinit function */ + 0x0102, /* version = 1.2 */ + NULL, /* no status variables */ + NULL, /* no system variables */ + "1.2", /* String version representation */ + MariaDB_PLUGIN_MATURITY_STABLE /* Maturity (see include/mysql/plugin.h)*/ +} +mysql_declare_plugin_end; + +} diff --git a/plugin/disks/mysql-test/disks/disks.result b/plugin/disks/mysql-test/disks/disks.result new file mode 100644 index 00000000..888f2df6 --- /dev/null +++ b/plugin/disks/mysql-test/disks/disks.result @@ -0,0 +1,12 @@ +show create table information_schema.disks; +Table Create Table +DISKS CREATE TEMPORARY TABLE `DISKS` ( + `Disk` varchar(pathlen) NOT NULL, + `Path` varchar(pathlen) NOT NULL, + `Total` bigint(32) NOT NULL, + `Used` bigint(32) NOT NULL, + `Available` bigint(32) NOT NULL +) ENGINE=MEMORY DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci +select sum(Total) > sum(Available), sum(Total)>sum(Used) from information_schema.disks; +sum(Total) > sum(Available) sum(Total)>sum(Used) +1 1 diff --git a/plugin/disks/mysql-test/disks/disks.test b/plugin/disks/mysql-test/disks/disks.test new file mode 100644 index 00000000..7189c548 --- /dev/null +++ b/plugin/disks/mysql-test/disks/disks.test @@ -0,0 +1,3 @@ +--replace_regex /varchar\([0-9]+\)/varchar(pathlen)/ +show create table information_schema.disks; +select sum(Total) > sum(Available), sum(Total)>sum(Used) from information_schema.disks; diff --git a/plugin/disks/mysql-test/disks/disks_notembedded.result b/plugin/disks/mysql-test/disks/disks_notembedded.result new file mode 100644 index 00000000..97429474 --- /dev/null +++ b/plugin/disks/mysql-test/disks/disks_notembedded.result @@ -0,0 +1,22 @@ +# +# MDEV-18328: Make DISKS plugin check some privilege to access +# information_schema.DISKS table +# +CREATE USER user1@localhost; +GRANT SELECT ON *.* TO user1@localhost; +connect con1,localhost,user1,,; +connection con1; +select sum(Total) > sum(Available), sum(Total)>sum(Used) from information_schema.disks; +sum(Total) > sum(Available) sum(Total)>sum(Used) +NULL NULL +disconnect con1; +connection default; +GRANT FILE ON *.* TO user1@localhost; +connect con1,localhost,user1,,; +connection con1; +select sum(Total) > sum(Available), sum(Total)>sum(Used) from information_schema.disks; +sum(Total) > sum(Available) sum(Total)>sum(Used) +1 1 +connection default; +DROP USER user1@localhost; +# End of 10.1 tests diff --git a/plugin/disks/mysql-test/disks/disks_notembedded.test b/plugin/disks/mysql-test/disks/disks_notembedded.test new file mode 100644 index 00000000..a0f6c2e5 --- /dev/null +++ b/plugin/disks/mysql-test/disks/disks_notembedded.test @@ -0,0 +1,25 @@ +source include/not_embedded.inc; + +--echo # +--echo # MDEV-18328: Make DISKS plugin check some privilege to access +--echo # information_schema.DISKS table +--echo # + +CREATE USER user1@localhost; +GRANT SELECT ON *.* TO user1@localhost; + +connect (con1,localhost,user1,,); +connection con1; +select sum(Total) > sum(Available), sum(Total)>sum(Used) from information_schema.disks; +disconnect con1; + +connection default; +GRANT FILE ON *.* TO user1@localhost; + +connect (con1,localhost,user1,,); +connection con1; +select sum(Total) > sum(Available), sum(Total)>sum(Used) from information_schema.disks; +connection default; +DROP USER user1@localhost; + +--echo # End of 10.1 tests diff --git a/plugin/disks/mysql-test/disks/suite.opt b/plugin/disks/mysql-test/disks/suite.opt new file mode 100644 index 00000000..afbbe2b0 --- /dev/null +++ b/plugin/disks/mysql-test/disks/suite.opt @@ -0,0 +1 @@ +--plugin-load-add=$DISKS_SO diff --git a/plugin/disks/mysql-test/disks/suite.pm b/plugin/disks/mysql-test/disks/suite.pm new file mode 100644 index 00000000..c64ef3b3 --- /dev/null +++ b/plugin/disks/mysql-test/disks/suite.pm @@ -0,0 +1,10 @@ +package My::Suite::Disks; + +@ISA = qw(My::Suite); + +return "No Disks plugin" unless $ENV{DISKS_SO}; + +sub is_default { 1 } + +bless { }; + |