1 module unde.lsblk;
2 
3 import unde.lib;
4 import unde.slash;
5 
6 import std.conv;
7 import std..string;
8 import std.process;
9 import std.array;
10 import std.regex;
11 
12 version (Windows)
13 {
14 import core.sys.windows.winbase;
15 import core.sys.windows.winnt;
16 import core.stdc.stdarg;
17 import std.utf;
18 import std.stdio;
19 import std.format;
20 }
21 
22 struct LsblkInfo{
23     string name;
24     string mountpoint;
25     string fstype;
26     string label;
27     string uuid;
28     ulong  size;
29     ulong  used;
30     ulong  avail;
31 }
32 
33 version (Posix)
34 {
35 private string slashXToString(string str)
36 {
37     string res = "";
38     for (int i=0; i < str.length; i++)
39     {
40         if (str.length - i >= 4)
41         {
42             if (str[i] == '\\' && str[i+1] == 'x')
43             {
44                 string s;
45                 res ~= parse!ubyte(s=str[i+2..i+4], 16);
46                 i+=3;
47                 continue;
48             }
49         }
50         res ~= str[i];
51     }
52     return res;
53 }
54 
55 unittest
56 {
57     assert(slashXToString("a\\x20b") == "a b");
58     assert(slashXToString("ab\\x20") == "ab ");
59     assert(slashXToString("ab\\x2") == "ab\\x2");
60 }
61 
62 void df(ref LsblkInfo lsblk_info)
63 {
64     auto df_pipes = pipeProcess(["df", "--output=size,used,avail", lsblk_info.mountpoint], Redirect.stdout);
65     scope(exit) wait(df_pipes.pid);
66 
67     int l=0;
68     foreach (df_line; df_pipes.stdout.byLine)
69     {
70         if (l == 1)
71         {
72             auto match = matchFirst(df_line, regex(`(\d+) +(\d+) +(\d+)`));
73             if (match)
74             {
75                 lsblk_info.size = to!ulong(match[1])*1024;
76                 lsblk_info.used = to!ulong(match[2])*1024;
77                 lsblk_info.avail = to!ulong(match[3])*1024;
78             }
79         }
80         l++;
81     }
82 }
83 }
84 
85 version(Windows)
86 {
87 void df(ref LsblkInfo lsblk_info)
88 {
89 	uint sectors_per_cluster;
90 	uint bytes_per_sector;
91 	uint number_of_free_clusters;
92 	uint total_number_of_clusters;
93 
94 	auto res = GetDiskFreeSpace(
95 			toUTF16z(lsblk_info.name~"\\"),
96 			&sectors_per_cluster,
97 			&bytes_per_sector,
98 			&number_of_free_clusters,
99 			&total_number_of_clusters);
100 	if (!res)
101 	{
102 		throw new Exception("GetDiskFreeSpace: "~GetErrorMessage());
103 	}
104 
105 	lsblk_info.size = cast(ulong)total_number_of_clusters * sectors_per_cluster * bytes_per_sector;
106 	lsblk_info.avail = cast(ulong)number_of_free_clusters * sectors_per_cluster * bytes_per_sector;
107 	lsblk_info.used = lsblk_info.size - lsblk_info.avail;
108 }
109 }
110 
111 void lsblk(ref LsblkInfo[string] lsblk)
112 {
113     version(Posix)
114     {
115     auto pipes = pipeProcess(["lsblk", "-rno", "NAME,MOUNTPOINT,FSTYPE,LABEL,UUID"], Redirect.stdout);
116     scope(exit) wait(pipes.pid);
117 
118     foreach (line; pipes.stdout.byLine)
119     {
120         LsblkInfo lsblk_info;
121 
122         foreach(i, arg; split(line, " "))
123         {
124             string im_arg = arg.idup();
125             switch (i)
126             {
127                 case 0:
128                     lsblk_info.name = slashXToString(im_arg);
129                     break;
130                 case 1:
131                     lsblk_info.mountpoint = slashXToString(im_arg);
132                     break;
133                 case 2:
134                     lsblk_info.fstype = slashXToString(im_arg);
135                     break;
136                 case 3:
137                     lsblk_info.label = slashXToString(im_arg);
138                     break;
139                 case 4:
140                     lsblk_info.uuid = slashXToString(im_arg);
141                     break;
142                 default:
143                     assert(0, "lsblk returned line with more than 2 spaces");
144             }
145         }
146 
147         if (lsblk_info.mountpoint > "" && lsblk_info.mountpoint != "[SWAP]")
148         {
149             df(lsblk_info);
150             lsblk[lsblk_info.mountpoint] = lsblk_info;
151         }
152     }
153     }
154     else version(Windows)
155     {
156         LsblkInfo lsblk_info;
157         lsblk_info.name = "My Computer";
158         lsblk_info.mountpoint = SL;
159         lsblk_info.uuid = "my_computer";
160         lsblk[lsblk_info.mountpoint] = lsblk_info;
161         LsblkInfo *mycomp_info = &lsblk[lsblk_info.mountpoint];
162 
163 	auto drives = GetLogicalDrives();
164 	if (drives == 0)
165 	{
166 		throw new Exception("GetLogicalDrives: "~GetErrorMessage());
167 	}
168 	foreach (i; 0..32)
169 	{
170 		char letter = cast(char)('A' + i);
171 		if (drives & (1 << i))
172 		{
173 			wchar[1024] volumename;
174 			uint serialnumber;
175 			uint maximum_components_length;
176 			uint file_system_flags;
177 			wchar[1024] file_system_name;
178 
179 			auto res = GetVolumeInformation(
180 					toUTF16z(letter~":\\"),
181 					volumename.ptr,
182 					volumename.length,
183 					&serialnumber,
184 					&maximum_components_length,
185 					&file_system_flags,
186 					file_system_name.ptr,
187 					file_system_name.length);
188 			lsblk_info = LsblkInfo();
189 			lsblk_info.name = letter~":";
190 			lsblk_info.mountpoint = letter~":";
191 			if (res)
192 			{
193 				lsblk_info.fstype = to!(char[])(from_char_array(file_system_name));
194 				lsblk_info.uuid = format("%X", serialnumber);
195 				lsblk_info.label = to!string(from_char_array(volumename));
196 
197 				df(lsblk_info);
198 
199 				/*writefln("%s:", letter);
200 				writefln("\tvolumename=%s", to!string(from_char_array(volumename)));
201 				writefln("\tserialnumber=%X", serialnumber);
202 				writefln("\tmaximum_components_length=%d", maximum_components_length);
203 				writefln("\tfile_system_flags=%X", file_system_flags);
204 				writefln("\tfile_system_name=%s", to!(char[])(from_char_array(file_system_name)));
205 				writefln("\tsize=%s", lsblk_info.size);
206 				writefln("\tused=%s", lsblk_info.used);
207 				writefln("\tavail=%s", lsblk_info.avail);*/
208 
209 				mycomp_info.size += lsblk_info.size;
210 				mycomp_info.used += lsblk_info.used;
211 				mycomp_info.avail += lsblk_info.avail;
212 			}
213 			else
214 			{
215 				lsblk_info.label = GetErrorMessage();
216 				//writefln("%s: %s", letter, lsblk_info.label);
217 			}
218 			lsblk[lsblk_info.mountpoint] = lsblk_info;
219 
220 		}
221 	}
222     }
223 }