mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
305 lines
7.2 KiB
C
305 lines
7.2 KiB
C
/* virt-df
|
|
* Copyright (C) 2010-2012 Red Hat Inc.
|
|
*
|
|
* 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; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* 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-1301 USA.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <inttypes.h>
|
|
#include <xvasprintf.h>
|
|
#include <libintl.h>
|
|
#include <math.h>
|
|
#include <assert.h>
|
|
|
|
#ifdef HAVE_LIBVIRT
|
|
#include <libvirt/libvirt.h>
|
|
#include <libvirt/virterror.h>
|
|
#endif
|
|
|
|
#include "c-ctype.h"
|
|
#include "human.h"
|
|
#include "progname.h"
|
|
|
|
#include "guestfs.h"
|
|
#include "options.h"
|
|
#include "virt-df.h"
|
|
|
|
static void write_csv_field (const char *field);
|
|
|
|
void
|
|
print_title (void)
|
|
{
|
|
const char *cols[6];
|
|
|
|
cols[0] = _("VirtualMachine");
|
|
cols[1] = _("Filesystem");
|
|
if (!inodes) {
|
|
if (!human)
|
|
cols[2] = _("1K-blocks");
|
|
else
|
|
cols[2] = _("Size");
|
|
cols[3] = _("Used");
|
|
cols[4] = _("Available");
|
|
cols[5] = _("Use%");
|
|
} else {
|
|
cols[2] = _("Inodes");
|
|
cols[3] = _("IUsed");
|
|
cols[4] = _("IFree");
|
|
cols[5] = _("IUse%");
|
|
}
|
|
|
|
if (!csv) {
|
|
/* ignore cols[0] in this mode */
|
|
printf ("%-36s%10s %10s %10s %5s\n",
|
|
cols[1], cols[2], cols[3], cols[4], cols[5]);
|
|
}
|
|
else {
|
|
size_t i;
|
|
|
|
for (i = 0; i < 6; ++i) {
|
|
if (i > 0)
|
|
putchar (',');
|
|
write_csv_field (cols[i]);
|
|
}
|
|
putchar ('\n');
|
|
}
|
|
}
|
|
|
|
static char *adjust_device_offset (const char *device, int offset);
|
|
|
|
void
|
|
print_stat (const char *name, const char *uuid_param,
|
|
const char *dev_param, int offset,
|
|
const struct guestfs_statvfs *stat)
|
|
{
|
|
/* First two columns are always 'name' and 'dev', followed by four
|
|
* other data columns. In text mode the 'name' and 'dev' are
|
|
* combined into a single 'name:dev' column. In CSV mode they are
|
|
* kept as two separate columns. In UUID mode the name might be
|
|
* replaced by 'uuid', if available.
|
|
*/
|
|
#define MAX_LEN (LONGEST_HUMAN_READABLE > 128 ? LONGEST_HUMAN_READABLE : 128)
|
|
char buf[4][MAX_LEN];
|
|
const char *cols[4];
|
|
int64_t factor, v;
|
|
float percent;
|
|
int hopts = human_round_to_nearest|human_autoscale|human_base_1024|human_SI;
|
|
size_t i, len;
|
|
char *dev;
|
|
|
|
/* Make a canonical name, adjusting the device offset if necessary. */
|
|
dev = guestfs_canonical_device_name (g, dev_param);
|
|
if (!dev)
|
|
exit (EXIT_FAILURE);
|
|
if (offset >= 0) {
|
|
char *p = dev;
|
|
dev = adjust_device_offset (p, offset);
|
|
free (p);
|
|
}
|
|
|
|
if (!inodes) { /* 1K blocks */
|
|
if (!human) {
|
|
factor = stat->bsize / 1024;
|
|
|
|
v = stat->blocks * factor;
|
|
snprintf (buf[0], MAX_LEN, "%" PRIi64, v);
|
|
cols[0] = buf[0];
|
|
v = (stat->blocks - stat->bfree) * factor;
|
|
snprintf (buf[1], MAX_LEN, "%" PRIi64, v);
|
|
cols[1] = buf[1];
|
|
v = stat->bavail * factor;
|
|
snprintf (buf[2], MAX_LEN, "%" PRIi64, v);
|
|
cols[2] = buf[2];
|
|
} else {
|
|
cols[0] =
|
|
human_readable ((uintmax_t) stat->blocks, buf[0],
|
|
hopts, stat->bsize, 1);
|
|
v = stat->blocks - stat->bfree;
|
|
cols[1] =
|
|
human_readable ((uintmax_t) v, buf[1], hopts, stat->bsize, 1);
|
|
cols[2] =
|
|
human_readable ((uintmax_t) stat->bavail, buf[2],
|
|
hopts, stat->bsize, 1);
|
|
}
|
|
|
|
if (stat->blocks != 0)
|
|
percent = 100. - 100. * stat->bfree / stat->blocks;
|
|
else
|
|
percent = 0;
|
|
}
|
|
else { /* inodes */
|
|
snprintf (buf[0], MAX_LEN, "%" PRIi64, stat->files);
|
|
cols[0] = buf[0];
|
|
snprintf (buf[1], MAX_LEN, "%" PRIi64, stat->files - stat->ffree);
|
|
cols[1] = buf[1];
|
|
snprintf (buf[2], MAX_LEN, "%" PRIi64, stat->ffree);
|
|
cols[2] = buf[2];
|
|
|
|
if (stat->files != 0)
|
|
percent = 100. - 100. * stat->ffree / stat->files;
|
|
else
|
|
percent = 0;
|
|
}
|
|
|
|
if (!csv)
|
|
/* Use 'ceil' on the percentage in order to emulate what df itself does. */
|
|
snprintf (buf[3], MAX_LEN, "%3.0f%%", ceil (percent));
|
|
else
|
|
snprintf (buf[3], MAX_LEN, "%.1f", (double) percent);
|
|
cols[3] = buf[3];
|
|
|
|
#undef MAX_LEN
|
|
|
|
if (uuid && uuid_param)
|
|
name = uuid_param;
|
|
|
|
if (!csv) {
|
|
len = strlen (name) + strlen (dev) + 1;
|
|
printf ("%s:%s", name, dev);
|
|
if (len <= 36) {
|
|
for (i = len; i < 36; ++i)
|
|
putchar (' ');
|
|
} else {
|
|
printf ("\n ");
|
|
}
|
|
|
|
printf ("%10s %10s %10s %5s\n", cols[0], cols[1], cols[2], cols[3]);
|
|
}
|
|
else {
|
|
write_csv_field (name);
|
|
putchar (',');
|
|
write_csv_field (dev);
|
|
|
|
for (i = 0; i < 4; ++i) {
|
|
putchar (',');
|
|
write_csv_field (cols[i]);
|
|
}
|
|
|
|
putchar ('\n');
|
|
}
|
|
|
|
free (dev);
|
|
}
|
|
|
|
/* Function to quote CSV fields on output without requiring an
|
|
* external module.
|
|
*/
|
|
static void
|
|
write_csv_field (const char *field)
|
|
{
|
|
size_t i, len;
|
|
int needs_quoting = 0;
|
|
|
|
len = strlen (field);
|
|
|
|
for (i = 0; i < len; ++i) {
|
|
if (field[i] == ' ' || field[i] == '"' ||
|
|
field[i] == '\n' || field[i] == ',') {
|
|
needs_quoting = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!needs_quoting) {
|
|
printf ("%s", field);
|
|
return;
|
|
}
|
|
|
|
/* Quoting for CSV fields. */
|
|
putchar ('"');
|
|
for (i = 0; i < len; ++i) {
|
|
if (field[i] == '"') {
|
|
putchar ('"');
|
|
putchar ('"');
|
|
} else
|
|
putchar (field[i]);
|
|
}
|
|
putchar ('"');
|
|
}
|
|
|
|
static char *drive_name (int index, char *ret);
|
|
|
|
static char *
|
|
adjust_device_offset (const char *device, int offset)
|
|
{
|
|
int index;
|
|
int part_num;
|
|
char *whole_device;
|
|
int free_whole_device;
|
|
size_t len;
|
|
char *ret;
|
|
|
|
/* Could be a whole disk or a partition. guestfs_device_index will
|
|
* only work with the whole disk name.
|
|
*/
|
|
len = strlen (device);
|
|
if (len > 0 && c_isdigit (device[len-1])) {
|
|
whole_device = guestfs_part_to_dev (g, device);
|
|
if (whole_device == NULL)
|
|
exit (EXIT_FAILURE);
|
|
free_whole_device = 1;
|
|
part_num = guestfs_part_to_partnum (g, device);
|
|
if (part_num == -1)
|
|
exit (EXIT_FAILURE);
|
|
} else {
|
|
whole_device = (char *) device;
|
|
free_whole_device = 0;
|
|
part_num = 0;
|
|
}
|
|
|
|
index = guestfs_device_index (g, whole_device);
|
|
if (index == -1)
|
|
exit (EXIT_FAILURE);
|
|
|
|
if (free_whole_device)
|
|
free (whole_device);
|
|
|
|
assert (index >= offset);
|
|
|
|
index -= offset;
|
|
|
|
/* Construct the final device name. */
|
|
ret = malloc (128);
|
|
if (!ret) {
|
|
perror ("malloc");
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
strcpy (ret, "/dev/sd");
|
|
drive_name (index, &ret[7]);
|
|
len = strlen (ret);
|
|
if (part_num > 0)
|
|
snprintf (&ret[len], 128-len, "%d", part_num);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* https://rwmj.wordpress.com/2011/01/09/how-are-linux-drives-named-beyond-drive-26-devsdz/ */
|
|
static char *
|
|
drive_name (int index, char *ret)
|
|
{
|
|
if (index >= 26)
|
|
ret = drive_name (index/26 - 1, ret);
|
|
index %= 26;
|
|
*ret++ = 'a' + index;
|
|
*ret = '\0';
|
|
return ret;
|
|
}
|