b60d285c26fea5901344f19e7e45873001fd989b
[libguestfs.git] / df / output.c
1 /* virt-df
2  * Copyright (C) 2010 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdint.h>
24 #include <inttypes.h>
25 #include <xvasprintf.h>
26 #include <math.h>
27 #include <assert.h>
28
29 #ifdef HAVE_LIBVIRT
30 #include <libvirt/libvirt.h>
31 #include <libvirt/virterror.h>
32 #endif
33
34 #include "c-ctype.h"
35 #include "human.h"
36 #include "progname.h"
37
38 #include "guestfs.h"
39 #include "options.h"
40 #include "virt-df.h"
41
42 static void write_csv_field (const char *field);
43
44 void
45 print_title (void)
46 {
47   const char *cols[6];
48
49   cols[0] = _("VirtualMachine");
50   cols[1] = _("Filesystem");
51   if (!inodes) {
52     if (!human)
53       cols[2] = _("1K-blocks");
54     else
55       cols[2] = _("Size");
56     cols[3] = _("Used");
57     cols[4] = _("Available");
58     cols[5] = _("Use%");
59   } else {
60     cols[2] = _("Inodes");
61     cols[3] = _("IUsed");
62     cols[4] = _("IFree");
63     cols[5] = _("IUse%");
64   }
65
66   if (!csv) {
67     /* ignore cols[0] in this mode */
68     printf ("%-36s%10s %10s %10s %5s\n",
69             cols[1], cols[2], cols[3], cols[4], cols[5]);
70   }
71   else {
72     size_t i;
73
74     for (i = 0; i < 6; ++i) {
75       if (i > 0)
76         putchar (',');
77       write_csv_field (cols[i]);
78     }
79     putchar ('\n');
80   }
81 }
82
83 static void canonical_device (char *dev, int offset);
84
85 void
86 print_stat (const char *name, const char *uuid_param,
87             const char *dev_param, int offset,
88             const struct guestfs_statvfs *stat)
89 {
90   /* First two columns are always 'name' and 'dev', followed by four
91    * other data columns.  In text mode the 'name' and 'dev' are
92    * combined into a single 'name:dev' column.  In CSV mode they are
93    * kept as two separate columns.  In UUID mode the name might be
94    * replaced by 'uuid', if available.
95    */
96 #define MAX_LEN (LONGEST_HUMAN_READABLE > 128 ? LONGEST_HUMAN_READABLE : 128)
97   char buf[4][MAX_LEN];
98   const char *cols[4];
99   int64_t factor, v;
100   float percent;
101   int hopts = human_round_to_nearest|human_autoscale|human_base_1024|human_SI;
102   size_t i, len;
103
104   /* Make the device canonical. */
105   len = strlen (dev_param) + 1;
106   char dev[len];
107   strcpy (dev, dev_param);
108   if (offset >= 0)
109     canonical_device (dev, offset);
110
111   if (!inodes) {                /* 1K blocks */
112     if (!human) {
113       factor = stat->bsize / 1024;
114
115       v = stat->blocks * factor;
116       snprintf (buf[0], MAX_LEN, "%" PRIi64, v);
117       cols[0] = buf[0];
118       v = (stat->blocks - stat->bfree) * factor;
119       snprintf (buf[1], MAX_LEN, "%" PRIi64, v);
120       cols[1] = buf[1];
121       v = stat->bavail * factor;
122       snprintf (buf[2], MAX_LEN, "%" PRIi64, v);
123       cols[2] = buf[2];
124     } else {
125       cols[0] =
126         human_readable ((uintmax_t) stat->blocks, buf[0],
127                         hopts, stat->bsize, 1);
128       v = stat->blocks - stat->bfree;
129       cols[1] =
130         human_readable ((uintmax_t) v, buf[1], hopts, stat->bsize, 1);
131       cols[2] =
132         human_readable ((uintmax_t) stat->bavail, buf[2],
133                         hopts, stat->bsize, 1);
134     }
135
136     if (stat->blocks != 0)
137       percent = 100. - 100. * stat->bfree / stat->blocks;
138     else
139       percent = 0;
140   }
141   else {                        /* inodes */
142     snprintf (buf[0], MAX_LEN, "%" PRIi64, stat->files);
143     cols[0] = buf[0];
144     snprintf (buf[1], MAX_LEN, "%" PRIi64, stat->files - stat->ffree);
145     cols[1] = buf[1];
146     snprintf (buf[2], MAX_LEN, "%" PRIi64, stat->ffree);
147     cols[2] = buf[2];
148
149     if (stat->files != 0)
150       percent = 100. - 100. * stat->ffree / stat->files;
151     else
152       percent = 0;
153   }
154
155   if (!csv)
156     /* Use 'ceil' on the percentage in order to emulate what df itself does. */
157     snprintf (buf[3], MAX_LEN, "%3.0f%%", ceil (percent));
158   else
159     snprintf (buf[3], MAX_LEN, "%.1f", percent);
160   cols[3] = buf[3];
161
162 #undef MAX_LEN
163
164   if (uuid && uuid_param)
165     name = uuid_param;
166
167   if (!csv) {
168     len = strlen (name) + strlen (dev) + 1;
169     printf ("%s:%s", name, dev);
170     if (len <= 36) {
171       for (i = len; i < 36; ++i)
172         putchar (' ');
173     } else {
174       printf ("\n                                    ");
175     }
176
177     printf ("%10s %10s %10s %5s\n", cols[0], cols[1], cols[2], cols[3]);
178   }
179   else {
180     write_csv_field (name);
181     putchar (',');
182     write_csv_field (dev);
183
184     for (i = 0; i < 4; ++i) {
185       putchar (',');
186       write_csv_field (cols[i]);
187     }
188
189     putchar ('\n');
190   }
191 }
192
193 /* /dev/vda1 -> /dev/sda, adjusting the device offset. */
194 static void
195 canonical_device (char *dev, int offset)
196 {
197   if (STRPREFIX (dev, "/dev/") &&
198       (dev[5] == 'h' || dev[5] == 'v') &&
199       dev[6] == 'd' &&
200       c_isalpha (dev[7]) &&
201       (c_isdigit (dev[8]) || dev[8] == '\0')) {
202     dev[5] = 's';
203     dev[7] -= offset;
204   }
205 }
206
207 /* Function to quote CSV fields on output without requiring an
208  * external module.
209  */
210 static void
211 write_csv_field (const char *field)
212 {
213   size_t i, len;
214   int needs_quoting = 0;
215
216   len = strlen (field);
217
218   for (i = 0; i < len; ++i) {
219     if (field[i] == ' ' || field[i] == '"' ||
220         field[i] == '\n' || field[i] == ',') {
221       needs_quoting = 1;
222       break;
223     }
224   }
225
226   if (!needs_quoting) {
227     printf ("%s", field);
228     return;
229   }
230
231   /* Quoting for CSV fields. */
232   putchar ('"');
233   for (i = 0; i < len; ++i) {
234     if (field[i] == '"') {
235       putchar ('"');
236       putchar ('"');
237     } else
238       putchar (field[i]);
239   }
240   putchar ('"');
241 }