1
|
|
|
<?php |
2
|
|
|
// This file is part of BOINC. |
3
|
|
|
// http://boinc.berkeley.edu |
4
|
|
|
// Copyright (C) 2008 University of California |
5
|
|
|
// |
6
|
|
|
// BOINC is free software; you can redistribute it and/or modify it |
7
|
|
|
// under the terms of the GNU Lesser General Public License |
8
|
|
|
// as published by the Free Software Foundation, |
9
|
|
|
// either version 3 of the License, or (at your option) any later version. |
10
|
|
|
// |
11
|
|
|
// BOINC is distributed in the hope that it will be useful, |
12
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
13
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
14
|
|
|
// See the GNU Lesser General Public License for more details. |
15
|
|
|
// |
16
|
|
|
// You should have received a copy of the GNU Lesser General Public License |
17
|
|
|
// along with BOINC. If not, see <http://www.gnu.org/licenses/>. |
18
|
|
|
|
19
|
|
|
require_once("../inc/credit.inc"); |
20
|
|
|
require_once("../inc/stats_sites.inc"); |
21
|
|
|
require_once("../inc/boinc_db.inc"); |
22
|
|
|
require_once("../inc/user.inc"); |
23
|
|
|
|
24
|
|
|
function link_to_results($host) { |
25
|
|
|
if (!$host) return tra("No host"); |
26
|
|
|
$config = get_config(); |
27
|
|
|
if (!parse_bool($config, "show_results")) return tra("Unavailable"); |
28
|
|
|
$nresults = host_nresults($host); |
29
|
|
|
if (!$nresults) return "0"; |
30
|
|
|
return "<a href=results.php?hostid=$host->id>$nresults</a>"; |
31
|
|
|
} |
32
|
|
|
|
33
|
|
|
function sched_log_name($x) { |
34
|
|
|
if ($x == 0) return "NO_SUCH_LOG"; |
35
|
|
|
return gmdate('Y-m-d_H/Y-m-d_H:i', $x) . ".txt"; |
36
|
|
|
} |
37
|
|
|
|
38
|
|
|
function sched_log_link($x) { |
39
|
|
|
if (file_exists("sched_logs")) { |
40
|
|
|
return "<a href=\"../sched_logs/" . sched_log_name($x) . "\">" . time_str($x) . "</a>"; |
41
|
|
|
} else { |
42
|
|
|
return time_str($x); |
43
|
|
|
} |
44
|
|
|
} |
45
|
|
|
|
46
|
|
|
function location_form($host) { |
47
|
|
|
$none = "selected"; |
48
|
|
|
$h=$w=$s=$m=""; |
49
|
|
|
if ($host->venue == "home") $h = "selected"; |
50
|
|
|
if ($host->venue == "work") $w = "selected"; |
51
|
|
|
if ($host->venue == "school") $s = "selected"; |
52
|
|
|
$x = "<form action=host_venue_action.php> |
53
|
|
|
<input type=hidden name=hostid value=$host->id> |
54
|
|
|
<select class=\"form-control\" name=venue> |
55
|
|
|
<option value=\"\" $none>--- |
56
|
|
|
<option value=home $h>".tra("Home")." |
57
|
|
|
<option value=work $w>".tra("Work")." |
58
|
|
|
<option value=school $s>".tra("School")." |
59
|
|
|
</select> |
60
|
|
|
<p></p> |
61
|
|
|
<input class=\"btn btn-primary btn-sm\" type=submit value=\"".tra("Update location")."\"> |
62
|
|
|
</form> |
63
|
|
|
"; |
64
|
|
|
return $x; |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
function cross_project_links($host) { |
68
|
|
|
global $host_sites; |
69
|
|
|
$x = ""; |
70
|
|
|
foreach ($host_sites as $h) { |
71
|
|
|
$url = $h[0]; |
72
|
|
|
$name = $h[1]; |
73
|
|
|
$img = $h[2]; |
74
|
|
|
$x .= "<a href=$url".$host->host_cpid."><img class=\"icon\" border=2 src=img/$img alt=\"$name\"></a> "; |
75
|
|
|
} |
76
|
|
|
return $x; |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
// Show full-page description of $host. |
80
|
|
|
// If $user is non-null, it's both the owner of the host |
81
|
|
|
// and the logged in user (so show some extra fields) |
82
|
|
|
// |
83
|
|
|
function show_host($host, $user, $ipprivate) { |
84
|
|
|
global $config; |
85
|
|
|
start_table(); |
86
|
|
|
row1(tra("Computer information")); |
87
|
|
|
$anonymous = false; |
88
|
|
|
if ($user) { |
89
|
|
|
if ($ipprivate) { |
90
|
|
|
row2(tra("IP address"), "$host->last_ip_addr<br>".tra("(same the last %1 times)", $host->nsame_ip_addr)); |
91
|
|
|
if ($host->last_ip_addr != $host->external_ip_addr) { |
92
|
|
|
row2(tra("External IP address"), $host->external_ip_addr); |
93
|
|
|
} |
94
|
|
|
} else { |
95
|
|
|
row2(tra("IP address"), "<a href=show_host_detail.php?hostid=$host->id&ipprivate=1>".tra("Show IP address")."</a>"); |
96
|
|
|
} |
97
|
|
|
row2(tra("Domain name"), $host->domain_name); |
98
|
|
|
if ($host->product_name) { |
99
|
|
|
row2(tra("Product name"), $host->product_name); |
100
|
|
|
} |
101
|
|
|
$x = $host->timezone/3600; |
102
|
|
|
if ($x >= 0) $x="+$x"; |
103
|
|
|
row2(tra("Local Standard Time"), tra("UTC %1 hours", $x)); |
104
|
|
|
} else { |
105
|
|
|
$owner = BoincUser::lookup_id($host->userid); |
106
|
|
|
if ($owner && $owner->show_hosts) { |
107
|
|
|
row2(tra("Owner"), user_links($owner, BADGE_HEIGHT_MEDIUM)); |
108
|
|
|
} else { |
109
|
|
|
row2(tra("Owner"), tra("Anonymous")); |
110
|
|
|
$anonymous = true; |
111
|
|
|
} |
112
|
|
|
} |
113
|
|
|
row2(tra("Created"), time_str($host->create_time)); |
114
|
|
|
if (!NO_STATS) { |
115
|
|
|
row2(tra("Total credit"), format_credit_large($host->total_credit)); |
116
|
|
|
row2(tra("Average credit"), format_credit($host->expavg_credit)); |
117
|
|
|
if (!$anonymous) { |
118
|
|
|
row2(tra("Cross project credit"), cross_project_links($host)); |
119
|
|
|
} |
120
|
|
|
} |
121
|
|
|
row2(tra("CPU type"), "$host->p_vendor <br> $host->p_model"); |
122
|
|
|
row2(tra("Number of processors"), $host->p_ncpus); |
123
|
|
|
if ($host->serialnum) { |
124
|
|
|
row2(tra("Coprocessors"), gpu_desc($host->serialnum)); |
125
|
|
|
} |
126
|
|
|
row2(tra("Virtualization"), vbox_desc($host->serialnum)); |
127
|
|
|
row2(tra("Operating System"), "$host->os_name <br> $host->os_version"); |
128
|
|
|
$v = boinc_version($host->serialnum); |
129
|
|
|
if ($v) { |
130
|
|
|
row2(tra("BOINC version"), $v); |
131
|
|
|
} |
132
|
|
|
$x = $host->m_nbytes/GIGA; |
133
|
|
|
$y = round($x, 2); |
134
|
|
|
row2(tra("Memory"), tra("%1 GB", $y)); |
135
|
|
|
if ($host->m_cache > 0) { |
136
|
|
|
$x = $host->m_cache/KILO; |
137
|
|
|
$y = round($x, 2); |
138
|
|
|
row2(tra("Cache"), tra("%1 KB", $y)); |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
if ($user) { |
142
|
|
|
$x = $host->m_swap/GIGA; |
143
|
|
|
$y = round($x, 2); |
144
|
|
|
row2(tra("Swap space"), tra("%1 GB", $y)); |
145
|
|
|
$x = $host->d_total/GIGA; |
146
|
|
|
$y = round($x, 2); |
147
|
|
|
row2(tra("Total disk space"), tra("%1 GB", $y)); |
148
|
|
|
$x = $host->d_free/GIGA; |
149
|
|
|
$y = round($x, 2); |
150
|
|
|
row2(tra("Free Disk Space"), tra("%1 GB", $y)); |
151
|
|
|
} |
152
|
|
|
$x = $host->p_fpops/1e9; |
153
|
|
|
$y = round($x, 2); |
154
|
|
|
row2(tra("Measured floating point speed"), tra("%1 billion ops/sec", $y)); |
155
|
|
|
$x = $host->p_iops/1e9; |
156
|
|
|
$y = round($x, 2); |
157
|
|
|
row2(tra("Measured integer speed"), tra("%1 billion ops/sec", $y)); |
158
|
|
|
$x = $host->n_bwup/KILO; |
159
|
|
|
$y = round($x, 2); |
160
|
|
|
if ($y > 0) { |
161
|
|
|
row2(tra("Average upload rate"), tra("%1 KB/sec", $y)); |
162
|
|
|
} else { |
163
|
|
|
row2(tra("Average upload rate"), tra("Unknown")); |
164
|
|
|
} |
165
|
|
|
$x = $host->n_bwdown/KILO; |
166
|
|
|
$y = round($x, 2); |
167
|
|
|
if ($y > 0) { |
168
|
|
|
row2(tra("Average download rate"), tra("%1 KB/sec", $y)); |
169
|
|
|
} else { |
170
|
|
|
row2(tra("Average download rate"), tra("Unknown")); |
171
|
|
|
} |
172
|
|
|
$x = $host->avg_turnaround/86400; |
173
|
|
|
if (!NO_COMPUTING) { |
174
|
|
|
row2(tra("Average turnaround time"), tra("%1 days", round($x, 2))); |
175
|
|
|
row2(tra("Application details"), |
176
|
|
|
"<a href=host_app_versions.php?hostid=$host->id>".tra("Show")."</a>" |
177
|
|
|
); |
178
|
|
|
$show_results = parse_bool($config, "show_results"); |
179
|
|
|
if ($show_results) { |
180
|
|
|
$nresults = host_nresults($host); |
181
|
|
|
if ($nresults) { |
182
|
|
|
$results = "<a href=results.php?hostid=$host->id>$nresults</a>"; |
183
|
|
|
} else { |
184
|
|
|
$results = "0"; |
185
|
|
|
} |
186
|
|
|
row2(tra("Tasks"), $results); |
187
|
|
|
} |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
if ($user) { |
191
|
|
|
row2(tra("Number of times client has contacted server"), $host->rpc_seqno); |
192
|
|
|
row2(tra("Last time contacted server"), sched_log_link($host->rpc_time)); |
193
|
|
|
row2(tra("Fraction of time BOINC is running"), number_format(100*$host->on_frac, 2)."%"); |
194
|
|
|
if ($host->connected_frac > 0) { |
195
|
|
|
row2(tra("While BOINC is running, fraction of time computer has an Internet connection"), number_format(100*$host->connected_frac, 2)."%"); |
196
|
|
|
} |
197
|
|
|
row2(tra("While BOINC is running, fraction of time computing is allowed"), number_format(100*$host->active_frac, 2)."%"); |
198
|
|
|
row2(tra("While is BOINC running, fraction of time GPU computing is allowed"), number_format(100*$host->gpu_active_frac, 2)."%"); |
199
|
|
|
if ($host->cpu_efficiency) { |
200
|
|
|
row2(tra("Average CPU efficiency"), $host->cpu_efficiency); |
201
|
|
|
} |
202
|
|
|
if (!NO_COMPUTING) { |
203
|
|
|
if ($host->duration_correction_factor) { |
204
|
|
|
row2(tra("Task duration correction factor"), $host->duration_correction_factor); |
205
|
|
|
} |
206
|
|
|
} |
207
|
|
|
row2(tra("Location"), location_form($host)); |
208
|
|
|
if ($show_results && $nresults == 0) { |
209
|
|
|
$x = " · <a href=host_delete.php?hostid=$host->id".url_tokens($user->authenticator).">".tra("Delete this computer")."</a> "; |
210
|
|
|
} else { |
211
|
|
|
$x = ""; |
212
|
|
|
} |
213
|
|
|
row2(tra("Merge duplicate records of this computer"), "<a class=\"btn btn-primary btn-sm\" href=host_edit_form.php?hostid=$host->id>".tra("Merge")."</a> $x"); |
214
|
|
|
} else { |
215
|
|
|
row2(tra("Number of times client has contacted server"), $host->rpc_seqno); |
216
|
|
|
row2(tra("Last contact"), date_str($host->rpc_time)); |
217
|
|
|
} |
218
|
|
|
echo "</table>\n"; |
219
|
|
|
|
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
// the following is used for list of top hosts |
223
|
|
|
// |
224
|
|
|
function top_host_table_start($sort_by) { |
225
|
|
|
global $host_sites; |
226
|
|
|
shuffle($host_sites); |
227
|
|
|
start_table('table-striped'); |
228
|
|
|
$x = array( |
229
|
|
|
tra("Info"), |
230
|
|
|
tra("Rank"), |
231
|
|
|
tra("Owner"), |
232
|
|
|
); |
233
|
|
|
if (!NO_STATS) { |
234
|
|
|
if ($sort_by == 'total_credit') { |
235
|
|
|
$x[] = "<a href=top_hosts.php?sort_by=expavg_credit>".tra("Avg. credit")."</a>"; |
236
|
|
|
$x[] = tra("Total credit"); |
237
|
|
|
} else { |
238
|
|
|
$x[] = tra("Recent average credit"); |
239
|
|
|
$x[] = "<a href=top_hosts.php?sort_by=total_credit>".tra("Total credit")."</a>"; |
240
|
|
|
} |
241
|
|
|
} |
242
|
|
|
$x[] = tra("BOINC version"); |
243
|
|
|
$x[] = tra("CPU"); |
244
|
|
|
$x[] = tra("GPU"); |
245
|
|
|
$x[] = tra("Operating system"); |
246
|
|
|
$s = 'style="text-align:right;"'; |
247
|
|
|
$a = array("", "", "", $s, $s, "", "", "", ""); |
248
|
|
|
row_heading_array($x, $a, "bg-default"); |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
function host_nresults($host) { |
252
|
|
|
return BoincResult::count("hostid=$host->id"); |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
|
256
|
|
|
// Parse the Virtualbox version information from inside the serialnum field. |
257
|
|
|
// Prior to BOINC commit 6121ce1, the DB entry looked like e.g. "[vbox|5.0.0]" |
258
|
|
|
// where 5.0.0 gave the Virtualbox version number. After 6121ce1, the entry was |
259
|
|
|
// "[vbox|5.0.0|1|1]", where now two additional flags give information about |
260
|
|
|
// hardware virtualization support. Older clients may have the old-style |
261
|
|
|
// serialnum in the DB despite the server being upgraded. |
262
|
|
|
function vbox_desc($x){ |
263
|
|
|
if (preg_match("/\[vbox\|(.*?)(\|([01])\|([01]))?\]/",$x,$matches)){ |
264
|
|
|
$desc = "Virtualbox (".$matches[1].") ".tra("installed"); |
265
|
|
|
if (sizeof($matches)>2){ |
266
|
|
|
if ($matches[3]=="1" and $matches[4]=="1") { |
267
|
|
|
return $desc.tra(", CPU has hardware virtualization support and it is enabled"); |
268
|
|
|
} elseif ($matches[3]=="1" and $matches[4]=="0") { |
269
|
|
|
return $desc.tra(", CPU has hardware virtualization support but it is disabled"); |
270
|
|
|
} elseif ($matches[3]=="0") { |
271
|
|
|
return $desc.tra(", CPU does not have hardware virtualization support"); |
272
|
|
|
} |
273
|
|
|
} else { |
274
|
|
|
return $desc; |
275
|
|
|
} |
276
|
|
|
} else { |
277
|
|
|
return tra("None"); |
278
|
|
|
} |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
// Given string of the form [BOINC|vers][type|model|count|RAM|driver-vers][vbox|vers], |
282
|
|
|
// return a human-readable version of the GPU info |
283
|
|
|
// |
284
|
|
|
function gpu_desc($x, $detail=true) { |
285
|
|
|
$descs = explode("]", $x); |
286
|
|
|
array_pop($descs); |
287
|
|
|
$str = ""; |
288
|
|
|
foreach ($descs as $desc) { |
289
|
|
|
$desc = trim($desc, "["); |
290
|
|
|
$d = explode("|", $desc); |
291
|
|
|
//print_r($d); |
292
|
|
|
if ($d[0] == "BOINC") continue; |
293
|
|
|
if ($d[0] == "vbox") continue; |
294
|
|
|
if (count($d) < 5) continue; |
295
|
|
|
if ($str) $str .= "<p>"; |
296
|
|
|
if ($d[2]!="" && $d[2]!="1") $str .= "[".$d[2]."] "; |
297
|
|
|
if ($d[0] == "CUDA") { |
298
|
|
|
$str .= "NVIDIA"; |
299
|
|
|
} else if ($d[0] == "CAL") { |
300
|
|
|
$str .= "AMD"; |
301
|
|
|
} else if ($d[0] == "opencl_gpu") { |
302
|
|
|
$str .= "OpenCL GPU"; |
303
|
|
|
} else { |
304
|
|
|
$str .= $d[0]; |
305
|
|
|
} |
306
|
|
|
$str .= " ".$d[1]; |
307
|
|
|
if ($detail) { |
308
|
|
|
$str .= " (".$d[3].")"; |
309
|
|
|
if (array_key_exists(4, $d)) { |
310
|
|
|
if ($d[4] != "" && $d[4] != 0) { |
311
|
|
|
// if version has no '.', assume it's in 100*maj+min form |
312
|
|
|
// |
313
|
|
|
if (strchr($d[4], '.')) { |
314
|
|
|
$str .= " driver: ".$d[4]; |
315
|
|
|
} else { |
316
|
|
|
$i = (int)$d[4]; |
317
|
|
|
$maj = (int)($i/100); |
318
|
|
|
$min = $i%100; |
319
|
|
|
$str .= sprintf(" driver: %d.%02d", $maj, $min); |
320
|
|
|
} |
321
|
|
|
} |
322
|
|
|
} |
323
|
|
|
if (array_key_exists(5, $d)) { |
324
|
|
|
if ($d[5] != "" && $d[5] != 0) { |
325
|
|
|
if (strchr($d[5], '.')) { |
326
|
|
|
$str .= " OpenCL: ".$d[5]; |
327
|
|
|
} else { |
328
|
|
|
$i = (int)$d[5]; |
329
|
|
|
$maj = (int)($i/100); |
330
|
|
|
$min = $i%100; |
331
|
|
|
$str .= sprintf(" OpenCL: %d.%d", $maj, $min); |
332
|
|
|
} |
333
|
|
|
} |
334
|
|
|
} |
335
|
|
|
} |
336
|
|
|
} |
337
|
|
|
if (!$str) $str = "---"; |
338
|
|
|
return $str; |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
// Given the same string as above, return the BOINC version |
342
|
|
|
// |
343
|
|
|
function boinc_version($x) { |
344
|
|
|
$y = strstr($x, 'BOINC'); |
345
|
|
|
if (!$y) return ''; |
346
|
|
|
$z = explode("]", $y, 2); |
347
|
|
|
$a = explode('|', $z[0]); |
348
|
|
|
$v = $a[1]; |
349
|
|
|
if (array_key_exists(2, $a)) { |
350
|
|
|
$brand = $a[2]; |
351
|
|
|
$v .= " ($brand)"; |
352
|
|
|
} |
353
|
|
|
return $v; |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
function cpu_desc($host) { |
357
|
|
|
return "$host->p_vendor<br>$host->p_model<br>".tra("(%1 processors)", $host->p_ncpus)."\n"; |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
// If private is true, we're showing the host to its owner, |
361
|
|
|
// so it's OK to show the domain name etc. |
362
|
|
|
// If private is false, show the owner's name only if they've given permission |
363
|
|
|
// |
364
|
|
|
function show_host_row($host, $i, $private, $show_owner, $any_product_name) { |
365
|
|
|
$anonymous = false; |
366
|
|
|
if (!$private) { |
367
|
|
|
if ($show_owner) { |
368
|
|
|
$user = BoincUser::lookup_id($host->userid); |
369
|
|
|
if ($user && $user->show_hosts) { |
|
|
|
|
370
|
|
|
} else { |
371
|
|
|
$anonymous = true; |
372
|
|
|
} |
373
|
|
|
} |
374
|
|
|
} |
375
|
|
|
echo "<tr><td>ID: $host->id |
376
|
|
|
<br><a href=show_host_detail.php?hostid=$host->id>".tra("Details")."</a> |
377
|
|
|
"; |
378
|
|
|
if (!NO_COMPUTING) { |
379
|
|
|
echo " |
380
|
|
|
| <a href=results.php?hostid=$host->id>".tra("Tasks")."</a> |
381
|
|
|
"; |
382
|
|
|
} |
383
|
|
|
if (!NO_STATS) { |
384
|
|
|
if (!$anonymous) { |
385
|
|
|
echo " |
386
|
|
|
<br><nobr><small>".tra("Cross-project stats:")."</small></nobr><br>".cross_project_links($host); |
387
|
|
|
} |
388
|
|
|
} |
389
|
|
|
echo " |
390
|
|
|
</td> |
391
|
|
|
"; |
392
|
|
|
if ($private) { |
393
|
|
|
echo "<td>$host->domain_name</td>\n"; |
394
|
|
|
if ($any_product_name) { |
395
|
|
|
echo "<td>$host->product_name</td>\n"; |
396
|
|
|
} |
397
|
|
|
echo "<td>$host->venue</td>\n"; |
398
|
|
|
} else { |
399
|
|
|
echo "<td>$i</td>\n"; |
400
|
|
|
if ($show_owner) { |
401
|
|
|
if ($anonymous) { |
402
|
|
|
echo "<td>".tra("Anonymous")."</td>\n"; |
403
|
|
|
} else { |
404
|
|
|
echo "<td>", user_links($user, BADGE_HEIGHT_MEDIUM), "</td>\n"; |
405
|
|
|
} |
406
|
|
|
} |
407
|
|
|
} |
408
|
|
|
if ($show_owner) { |
409
|
|
|
// This is used in the "top computers" display |
410
|
|
|
// |
411
|
|
|
if (!NO_STATS) { |
412
|
|
|
printf(" |
413
|
|
|
<td align=right>%s</td> |
414
|
|
|
<td align=right>%s</td>", |
415
|
|
|
format_credit($host->expavg_credit), |
416
|
|
|
format_credit_large($host->total_credit) |
417
|
|
|
); |
418
|
|
|
} |
419
|
|
|
printf(" |
420
|
|
|
<td>%s</td> |
421
|
|
|
<td>%s</td> |
422
|
|
|
<td>%s</td> |
423
|
|
|
<td>%s <br> %s</td>", |
424
|
|
|
boinc_version($host->serialnum), |
425
|
|
|
cpu_desc($host), |
426
|
|
|
gpu_desc($host->serialnum), |
427
|
|
|
$host->os_name, $host->os_version |
428
|
|
|
); |
429
|
|
|
} else { |
430
|
|
|
// This is used to show the computers of a given user |
431
|
|
|
// |
432
|
|
|
if (!NO_STATS) { |
433
|
|
|
printf(" |
434
|
|
|
<td align=right>%s</td> |
435
|
|
|
<td align=right>%s</td>", |
436
|
|
|
format_credit($host->expavg_credit), |
437
|
|
|
format_credit_large($host->total_credit) |
438
|
|
|
); |
439
|
|
|
} |
440
|
|
|
printf(" |
441
|
|
|
<td>%s</td> |
442
|
|
|
<td>%s</td> |
443
|
|
|
<td>%s</td> |
444
|
|
|
<td>%s<br><small>%s</small></td> |
445
|
|
|
<td>%s</td> |
446
|
|
|
", |
447
|
|
|
boinc_version($host->serialnum), |
448
|
|
|
cpu_desc($host), |
449
|
|
|
gpu_desc($host->serialnum), |
450
|
|
|
$host->os_name, $host->os_version, |
451
|
|
|
sched_log_link($host->rpc_time) |
452
|
|
|
); |
453
|
|
|
} |
454
|
|
|
|
455
|
|
|
echo "</tr>\n"; |
456
|
|
|
} |
457
|
|
|
|
458
|
|
|
// Logic for deciding whether two host records might actually |
459
|
|
|
// be the same machine, based on CPU info |
460
|
|
|
// |
461
|
|
|
// p_vendor is typically either AuthenticAMD or GenuineIntel. |
462
|
|
|
// Over time we've changed the contents of p_model. |
463
|
|
|
// Some examples: |
464
|
|
|
// Intel(R) Core(TM)2 Duo CPU E7300 @ 2.66GHz [Family 6 Model 23 Stepping 6] |
465
|
|
|
// AMD Athlon(tm) II X2 250 Processor [Family 16 Model 6 Stepping 3] |
466
|
|
|
// Intel(R) Xeon(R) CPU X5650 @ 2.67GHz [x86 Family 6 Model 44 Stepping 2] |
467
|
|
|
// Intel(R) Core(TM) i5-2500K CPU @ 3.30GHz [Intel64 Family 6 Model 42 Stepping 7] |
468
|
|
|
// |
469
|
|
|
// in the last 2 cases, let's call x86 and Intel64 the "architecture" |
470
|
|
|
// |
471
|
|
|
// so, here's the policy: |
472
|
|
|
// |
473
|
|
|
// if p_ncpus different, return false |
474
|
|
|
// if p_vendor different, return false |
475
|
|
|
// if both have family/model/stepping info |
476
|
|
|
// if info disagrees, return false |
477
|
|
|
// if both have GHz info, and they disagree, return false |
478
|
|
|
// if both have architecture, and they disagree, return false |
479
|
|
|
// return true |
480
|
|
|
// if p_model different, return false |
481
|
|
|
// return true |
482
|
|
|
// |
483
|
|
|
|
484
|
|
|
// parse p_model to produce the following structure: |
485
|
|
|
// x->speed "3.00GHz" etc. or null |
486
|
|
|
// x->arch "x86" etc. or null |
487
|
|
|
// x->info "Family 6 Model 23 Stepping 6" etc. or null |
488
|
|
|
// |
489
|
|
|
function parse_model($model) { |
490
|
|
|
$y = explode(" ", $model); |
491
|
|
|
$x = new StdClass; |
492
|
|
|
$x->speed = null; |
493
|
|
|
$x->arch = null; |
494
|
|
|
$x->info = null; |
495
|
|
|
foreach ($y as $z) { |
496
|
|
|
if (strstr($z, "GHz")) $x->speed = $z; |
497
|
|
|
if (strstr($z, "MHz")) $x->speed = $z; |
498
|
|
|
} |
499
|
|
|
$pos1 = strpos($model, '['); |
500
|
|
|
if ($pos1 === false) return $x; |
501
|
|
|
$pos2 = strpos($model, ']'); |
502
|
|
|
if ($pos2 === false) return $x; |
503
|
|
|
$a = substr($model, $pos1+1, $pos2-$pos1-1); |
504
|
|
|
$y = explode(" ", $a); |
505
|
|
|
if (count($y) == 0) return $x; |
506
|
|
|
if ($y[0] == "Family") { |
507
|
|
|
$x->info = $a; |
508
|
|
|
} else { |
509
|
|
|
$x->arch = $y[0]; |
510
|
|
|
$x->info = substr($a, strlen($y[0])+1); |
511
|
|
|
} |
512
|
|
|
return $x; |
513
|
|
|
} |
514
|
|
|
|
515
|
|
|
function cpus_compatible($host1, $host2) { |
516
|
|
|
if ($host1->p_ncpus != $host2->p_ncpus) return false; |
517
|
|
|
if ($host1->p_vendor != $host2->p_vendor) return false; |
518
|
|
|
$x1 = parse_model($host1->p_model); |
519
|
|
|
$x2 = parse_model($host2->p_model); |
520
|
|
|
if ($x1->info && $x2->info) { |
521
|
|
|
if ($x1->info != $x2->info) return false; |
522
|
|
|
if ($x1->speed && $x2->speed) { |
523
|
|
|
if ($x1->speed != $x2->speed) return false; |
524
|
|
|
} |
525
|
|
|
if ($x1->arch && $x2->arch) { |
526
|
|
|
if ($x1->arch != $x2->arch) return false; |
527
|
|
|
} |
528
|
|
|
return true; |
529
|
|
|
} |
530
|
|
|
if ($host1->p_model != $host2->p_model) return false; |
531
|
|
|
return true; |
532
|
|
|
} |
533
|
|
|
|
534
|
|
|
// does one host strictly precede the other? |
535
|
|
|
// |
536
|
|
|
function times_disjoint($host1, $host2) { |
537
|
|
|
if ($host1->rpc_time < $host2->create_time) return true; |
538
|
|
|
if ($host2->rpc_time < $host1->create_time) return true; |
539
|
|
|
return false; |
540
|
|
|
} |
541
|
|
|
|
542
|
|
|
function os_compatible($host1, $host2) { |
543
|
|
|
if (strstr($host1->os_name, "Windows") && strstr($host2->os_name, "Windows")) return true; |
544
|
|
|
if (strstr($host1->os_name, "Linux") && strstr($host2->os_name, "Linux")) return true; |
545
|
|
|
if (strstr($host1->os_name, "Darwin") && strstr($host2->os_name, "Darwin")) return true; |
546
|
|
|
if (strstr($host1->os_name, "SunOS") && strstr($host2->os_name, "SunOS")) return true; |
547
|
|
|
if ($host1->os_name == $host2->os_name) return true; |
548
|
|
|
return false; |
549
|
|
|
} |
550
|
|
|
|
551
|
|
|
// Return true if it's possible that the two host records |
552
|
|
|
// correspond to the same host |
553
|
|
|
// NOTE: the cheat-proofing comes from checking |
554
|
|
|
// that their time intervals are disjoint. |
555
|
|
|
// So the CPU/OS checks don't have to be very strict. |
556
|
|
|
// |
557
|
|
|
function hosts_compatible($host1, $host2, $show_detail) { |
558
|
|
|
// A host is "new" if it has no credit and no results. |
559
|
|
|
// Skip disjoint-time check if one host or other is new |
560
|
|
|
// |
561
|
|
|
$new1 = !$host1->total_credit && !host_nresults($host1); |
562
|
|
|
$new2 = !$host2->total_credit && !host_nresults($host2); |
563
|
|
|
if (!$new1 && !$new2) { |
564
|
|
|
if (!times_disjoint($host1, $host2)) { |
565
|
|
|
if ($show_detail) { |
566
|
|
|
$c1 = date_str($host1->create_time); |
567
|
|
|
$r1 = date_str($host1->rpc_time); |
568
|
|
|
$c2 = date_str($host2->create_time); |
569
|
|
|
$r2 = date_str($host2->rpc_time); |
570
|
|
|
echo "<br>".tra("Host %1 has overlapping lifetime:", $host2->id)." ($c1 - $r1), ($c2 - $r2)"; |
571
|
|
|
} |
572
|
|
|
return false; |
573
|
|
|
} |
574
|
|
|
} |
575
|
|
|
if (!os_compatible($host1, $host2)) { |
576
|
|
|
if ($show_detail) { |
577
|
|
|
echo "<br>".tra("Host %1 has an incompatible OS:", $host2->id)." ($host1->os_name, $host2->os_name)\n"; |
578
|
|
|
} |
579
|
|
|
return false; |
580
|
|
|
} |
581
|
|
|
if (!cpus_compatible($host1, $host2)) { |
582
|
|
|
if ($show_detail) { |
583
|
|
|
echo "<br>".tra("Host %1 has an incompatible CPU:", $host2->id)." ($host1->p_vendor $host1->p_model, $host2->p_vendor $host2->p_model)\n"; |
584
|
|
|
} |
585
|
|
|
return false; |
586
|
|
|
} |
587
|
|
|
return true; |
588
|
|
|
} |
589
|
|
|
|
590
|
|
|
// recompute host's average credit by scanning results. |
591
|
|
|
// Could be expensive if lots of results! |
592
|
|
|
// |
593
|
|
|
function host_update_credit($hostid) { |
594
|
|
|
$total = 0; |
595
|
|
|
$avg = 0; |
596
|
|
|
$avg_time = 0; |
597
|
|
|
|
598
|
|
|
$results = BoincResult::enum("hostid=$hostid order by received_time"); |
599
|
|
|
foreach($results as $result) { |
600
|
|
|
if ($result->granted_credit <= 0) continue; |
601
|
|
|
$total += $result->granted_credit; |
602
|
|
|
|
603
|
|
|
update_average( |
604
|
|
|
$result->received_time, |
605
|
|
|
$result->sent_time, |
606
|
|
|
$result->granted_credit, |
607
|
|
|
$avg, |
608
|
|
|
$avg_time |
609
|
|
|
); |
610
|
|
|
|
611
|
|
|
//echo "<br>$avg\n"; |
612
|
|
|
} |
613
|
|
|
|
614
|
|
|
// do a final decay |
615
|
|
|
// |
616
|
|
|
$now = time(); |
617
|
|
|
update_average(now, 0, 0, $avg, $avg_time); |
618
|
|
|
|
619
|
|
|
$host = new BoincHost(); |
620
|
|
|
$host->id = hostid; |
621
|
|
|
$host->update("total_credit=$total, expavg_credit=$avg, expavg_time=$now"); |
622
|
|
|
} |
623
|
|
|
|
624
|
|
|
// decay a host's average credit |
625
|
|
|
// |
626
|
|
|
function host_decay_credit($host) { |
627
|
|
|
$avg = $host->expavg_credit; |
628
|
|
|
$avg_time = $host->expavg_time; |
629
|
|
|
$now = time(); |
630
|
|
|
update_average($now, 0, 0, $avg, $avg_time); |
631
|
|
|
$host->update("expavg_credit=$avg, expavg_time=$now"); |
632
|
|
|
} |
633
|
|
|
|
634
|
|
|
// if the host hasn't received new credit for ndays, |
635
|
|
|
// decay its average and return true |
636
|
|
|
// |
637
|
|
|
function host_inactive_ndays($host, $ndays) { |
638
|
|
|
$diff = time() - $host->expavg_time; |
639
|
|
|
if ($diff > $ndays*86400) { |
640
|
|
|
host_decay_credit($host); |
641
|
|
|
return true; |
642
|
|
|
} |
643
|
|
|
return false; |
644
|
|
|
} |
645
|
|
|
|
646
|
|
|
// invariant: old_host.create_time < new_host.create_time |
647
|
|
|
// |
648
|
|
|
function merge_hosts($old_host, $new_host) { |
649
|
|
|
if ($old_host->id == $new_host->id) { |
650
|
|
|
return tra("same host"); |
651
|
|
|
} |
652
|
|
|
if (!hosts_compatible($old_host, $new_host, false)) { |
653
|
|
|
return tra("Can't merge host %1 into %2 - they're incompatible", $old_host->id, $new_host->id); |
654
|
|
|
} |
655
|
|
|
|
656
|
|
|
echo "<br>".tra("Merging host %1 into host %2", $old_host->id, $new_host->id)."\n"; |
657
|
|
|
|
658
|
|
|
// decay the average credit of both hosts |
659
|
|
|
// |
660
|
|
|
$now = time(); |
661
|
|
|
update_average($now, 0, 0, $old_host->expavg_credit, $old_host->expavg_time); |
662
|
|
|
update_average($now, 0, 0, $new_host->expavg_credit, $new_host->expavg_time); |
663
|
|
|
|
664
|
|
|
// update the database: |
665
|
|
|
// - add credit from old to new host |
666
|
|
|
// - change results to refer to new host |
667
|
|
|
// - put old host in "zombie" state (userid=0, rpc_seqno=new host ID) |
668
|
|
|
// |
669
|
|
|
$total_credit = $old_host->total_credit + $new_host->total_credit; |
670
|
|
|
$recent_credit = $old_host->expavg_credit + $new_host->expavg_credit; |
671
|
|
|
$result = $new_host->update("total_credit=$total_credit, expavg_credit=$recent_credit, expavg_time=$now"); |
672
|
|
|
if (!$result) { |
673
|
|
|
return tra("Couldn't update credit of new computer"); |
674
|
|
|
} |
675
|
|
|
$result = BoincResult::update_aux("hostid=$new_host->id where hostid=$old_host->id"); |
676
|
|
|
if (!$result) { |
677
|
|
|
return tra("Couldn't update results"); |
678
|
|
|
} |
679
|
|
|
|
680
|
|
|
$result = $old_host->update("total_credit=0, expavg_credit=0, userid=0, rpc_seqno=$new_host->id"); |
681
|
|
|
if (!$result) { |
682
|
|
|
return tra("Couldn't retire old computer"); |
683
|
|
|
} |
684
|
|
|
echo "<br>".tra("Retired old computer %1", $old_host->id)."\n"; |
685
|
|
|
return 0; |
686
|
|
|
} |
687
|
|
|
|
688
|
|
|
//////////////// helper functions for hosts_user.php //////////////// |
689
|
|
|
|
690
|
|
|
function link_url($sort, $rev, $show_all) { |
691
|
|
|
global $userid; |
692
|
|
|
$x = $userid ? "&userid=$userid":""; |
693
|
|
|
return "hosts_user.php?sort=$sort&rev=$rev&show_all=$show_all$x"; |
694
|
|
|
} |
695
|
|
|
|
696
|
|
|
function link_url_rev($actual_sort, $sort, $rev, $show_all) { |
697
|
|
|
if ($actual_sort == $sort) { |
698
|
|
|
$rev = 1 - $rev; |
699
|
|
|
} |
700
|
|
|
return link_url($sort, $rev, $show_all); |
701
|
|
|
} |
702
|
|
|
|
703
|
|
|
function more_or_less($sort, $rev, $show_all) { |
704
|
|
|
echo "<p>"; |
705
|
|
|
if ($show_all) { |
706
|
|
|
$url = link_url($sort, $rev, 0); |
707
|
|
|
echo tra("Show:")." ".tra("All computers")." · <a href=$url>".tra("Only computers active in past 30 days")."</a>"; |
708
|
|
|
} else { |
709
|
|
|
$url = link_url($sort, $rev, 1); |
710
|
|
|
echo tra("Show:")." <a href=$url>".tra("All computers")."</a> · ".tra("Only computers active in past 30 days"); |
711
|
|
|
} |
712
|
|
|
echo "<p>"; |
713
|
|
|
} |
714
|
|
|
|
715
|
|
|
function user_host_table_start( |
716
|
|
|
$private, $sort, $rev, $show_all, $any_product_name |
717
|
|
|
) { |
718
|
|
|
start_table('table-striped'); |
719
|
|
|
$x = array(); |
720
|
|
|
$a = array(); |
721
|
|
|
|
722
|
|
|
$url = link_url_rev($sort, "id", $rev, $show_all); |
723
|
|
|
$x[] = "<a href=$url>".tra("Computer ID")."</a>"; |
724
|
|
|
$a[] = ''; |
725
|
|
|
|
726
|
|
|
if ($private) { |
727
|
|
|
$url = link_url_rev($sort, "name", $rev, $show_all); |
728
|
|
|
$x[] = "<a href=$url>".tra("Name")."</a>"; |
729
|
|
|
$a[] = null; |
730
|
|
|
$url = link_url_rev($sort, "venue", $rev, $show_all); |
731
|
|
|
if ($any_product_name) { |
732
|
|
|
$x[] = tra("Model"); |
733
|
|
|
$a[] = null; |
734
|
|
|
} |
735
|
|
|
$x[] = "<a href=$url>".tra("Location")."</a>"; |
736
|
|
|
$a[] = null; |
737
|
|
|
} else { |
738
|
|
|
$x[] = tra("Rank"); |
739
|
|
|
$a[] = null; |
740
|
|
|
} |
741
|
|
|
if (!NO_STATS) { |
742
|
|
|
$url = link_url_rev($sort, "expavg_credit", $rev, $show_all); |
743
|
|
|
$x[] = "<a href=$url>".tra("Avg. credit")."</a>"; |
744
|
|
|
$a[] = ALIGN_RIGHT; |
745
|
|
|
$url = link_url_rev($sort, "total_credit", $rev, $show_all); |
746
|
|
|
$x[] = "<a href=$url>".tra("Total credit")."</a>"; |
747
|
|
|
$a[] = ALIGN_RIGHT; |
748
|
|
|
} |
749
|
|
|
$x[] = tra("BOINC<br>version"); |
750
|
|
|
$a[] = null; |
751
|
|
|
$url = link_url_rev($sort, "cpu", $rev, $show_all); |
752
|
|
|
$x[] = "<a href=$url>".tra("CPU")."</a>"; |
753
|
|
|
$a[] = null; |
754
|
|
|
$x[] = tra("GPU"); |
755
|
|
|
$a[] = null; |
756
|
|
|
$url = link_url_rev($sort, "os", $rev, $show_all); |
757
|
|
|
$x[] = "<a href=$url>".tra("Operating System")."</a>"; |
758
|
|
|
$a[] = null; |
759
|
|
|
$url = link_url_rev($sort, "rpc_time", $rev, $show_all); |
760
|
|
|
$x[] = "<a href=$url>".tra("Last contact")."</a>"; |
761
|
|
|
$a[] = null; |
762
|
|
|
row_heading_array($x, $a, "bg-default"); |
763
|
|
|
} |
764
|
|
|
|
765
|
|
|
function show_user_hosts($userid, $private, $show_all, $sort, $rev) { |
766
|
|
|
$desc = false; // whether the sort order's default is decreasing |
767
|
|
|
switch ($sort) { |
768
|
|
|
case "total_credit": $sort_clause = "total_credit"; $desc = true; break; |
769
|
|
|
case "expavg_credit": $sort_clause = "expavg_credit"; $desc = true; break; |
770
|
|
|
case "name": $sort_clause = "domain_name"; break; |
771
|
|
|
case "id": $sort_clause = "id"; break; |
772
|
|
|
case "cpu": $sort_clause = "p_vendor"; break; |
773
|
|
|
case "gpu": $sort_clause = "serialnum"; break; |
774
|
|
|
case "os": $sort_clause = "os_name"; break; |
775
|
|
|
case "venue": $sort_clause = "venue"; break; |
776
|
|
|
default: |
777
|
|
|
// default value -- sort by RPC time |
778
|
|
|
$sort = "rpc_time"; |
779
|
|
|
$sort_clause = "rpc_time"; |
780
|
|
|
$desc = true; |
781
|
|
|
} |
782
|
|
|
|
783
|
|
|
if ($rev != $desc) { |
784
|
|
|
$sort_clause .= " desc"; |
785
|
|
|
} |
786
|
|
|
more_or_less($sort, $rev, $show_all); |
787
|
|
|
|
788
|
|
|
$now = time(); |
789
|
|
|
$old_hosts=0; |
790
|
|
|
$i = 1; |
791
|
|
|
$hosts = BoincHost::enum("userid=$userid order by $sort_clause"); |
792
|
|
|
$any_product_name = false; |
793
|
|
|
foreach ($hosts as $host) { |
794
|
|
|
if ($host->product_name) { |
795
|
|
|
$any_product_name = true; |
796
|
|
|
break; |
797
|
|
|
} |
798
|
|
|
} |
799
|
|
|
user_host_table_start($private, $sort, $rev, $show_all, $any_product_name); |
800
|
|
|
foreach ($hosts as $host) { |
801
|
|
|
$is_old=false; |
802
|
|
|
if (($now - $host->rpc_time) > 30*86400) { |
803
|
|
|
$is_old=true; |
804
|
|
|
$old_hosts++; |
805
|
|
|
} |
806
|
|
|
if (!$show_all && $is_old) continue; |
807
|
|
|
show_host_row($host, $i, $private, false, $any_product_name); |
808
|
|
|
$i++; |
809
|
|
|
} |
810
|
|
|
end_table(); |
811
|
|
|
|
812
|
|
|
if ($old_hosts>0) { |
813
|
|
|
more_or_less($sort, $rev, $show_all); |
814
|
|
|
} |
815
|
|
|
|
816
|
|
|
if ($private) { |
817
|
|
|
echo " |
818
|
|
|
<a href=merge_by_name.php>".tra("Merge computers by name")."</a> |
819
|
|
|
"; |
820
|
|
|
} |
821
|
|
|
} |
822
|
|
|
|
823
|
|
|
// remove user-specific info from a user's hosts |
824
|
|
|
// |
825
|
|
|
function anonymize_hosts($user) { |
826
|
|
|
$hosts = BoincHost::enum("userid=$user->id"); |
827
|
|
|
foreach ($hosts as $h) { |
828
|
|
|
$h->update("domain_name='deleted', last_ip_addr=''"); |
829
|
|
|
} |
830
|
|
|
} |
831
|
|
|
|
832
|
|
|
$cvs_version_tracker[]="\$Id$"; //Generated automatically - do not edit |
833
|
|
|
|
834
|
|
|
?> |
835
|
|
|
|
This check looks for the bodies of
if
statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.These
if
bodies can be removed. If you have an empty if but statements in theelse
branch, consider inverting the condition.could be turned into
This is much more concise to read.