Project

General

Profile

Download (59.1 KB) Statistics
| Branch: | Revision:
1
#!/usr/bin/perl
2

    
3
# All rights reserved and Copyright (c) 2020 Origo Systems ApS.
4
# This file is provided with no warranty, and is subject to the terms and conditions defined in the license file LICENSE.md.
5
# The license file is part of this source code package and its content is also available at:
6
# https://www.origo.io/info/stabiledocs/licensing/stabile-open-source-license
7

    
8
package Stabile::Steamexec;
9

    
10
use Tie::DBI;
11
use URI::Escape;
12
use File::Basename;
13
use File::Copy;
14
use File::Rsync;
15
use Proc::ProcessTable;
16
use Sys::Syslog qw( :DEFAULT setlogsock);
17
use ConfigReader::Simple;
18
use Time::Local;
19
use XML::Simple;
20
use Data::Dumper;
21
use Data::UUID;
22
use lib '/var/www/stabile/cgi';
23
use Stabile;
24

    
25
my $arg1 = shift if $ARGV[0];
26
my $status = shift if $ARGV[0];
27
my $oldstatus = shift if $ARGV[0];
28
my $path1 = shift if $ARGV[0];
29
my $path2 = shift if $ARGV[0];
30
my $cmd1 =  shift if $ARGV[0];
31
my $cmd2 =  shift if $ARGV[0];
32
#my $cmd = join(" ", @ARGV); # The rest
33
my $res;
34
my $dsnap1;
35
my $fstab = "/etc/fstab";
36
my $debugfile = "/var/log/stabile/steamExec.out";
37
my $localpath;
38
my $localpath2;
39
my $localstatus;
40
my $localstatus2;
41
my $newvirtualsize;
42
my $newbackupsize;
43
my $newvirtualsize2;
44
my $newbackupsize2;
45
my $uimsg = '';
46

    
47
my $config = ConfigReader::Simple->new("/etc/stabile/config.cfg",
48
    [qw(RDIFF-BACKUP_ENABLED STORAGE_BACKUPDIR
49
    STORAGE_POOLS_ADDRESS_PATHS STORAGE_POOLS_LOCAL_PATHS
50
    STORAGE_POOLS_NAMES STORAGE_POOLS_DEFAULTS STORAGE_POOLS_RDIFF-BACKUP_ENABLED
51
    RDIFF-BACKUP_USERS DBI_USER DBI_PASSWD ENGINE_LINKED
52
    ENGINE_DATA_NIC)]);
53

    
54
$base = "/var/www/stabile";
55
$base = `cat /etc/stabile/basedir` if -e "/etc/stabile/basedir";
56
chomp $base;
57
my $logfile = "/var/log/stabile/steam.log";
58
my $dbiuser =  $config->get('DBI_USER') || "irigo";
59
my $dbipasswd = $config->get('DBI_PASSWD') || "";
60

    
61
my $backupdir = $config->get('STORAGE_BACKUPDIR') || "/mnt/stabile/backups";
62
my $tenders = $config->get('STORAGE_POOLS_ADDRESS_PATHS');
63
my @tenderlist = split(/,\s*/, $tenders);
64
my $tenderpaths = $config->get('STORAGE_POOLS_LOCAL_PATHS') || "/mnt/stabile/images";
65
my @tenderpathslist = split(/,\s*/, $tenderpaths);
66
my $tendernames = $config->get('STORAGE_POOLS_NAMES') || "Standard storage";
67
my @tendernameslist = split(/,\s*/, $tendernames);
68
my $opentcpports = $config->get('OPEN_TCP_PORTS');
69
my $openudpports = $config->get('OPEN_UDP_PORTS');
70

    
71
$sshcmd = "ssh -l irigo -i /var/www/.ssh/id_rsa_www -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no";
72

    
73
$current_time = time;
74
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($current_time);
75
$year += 1900;
76
$month = substr("0" . ($mon+1), -2);
77
$pretty_time = sprintf "%4d-%02d-%02d@%02d:%02d:%02d",$year,$mon+1,$mday,$hour,$min,$sec;
78

    
79
if (lc($arg1) eq "backupallimages" ) {
80
     debuglog("Backing up all images...");
81
     backupAllImages();
82
} elsif (lc($arg1) eq "backupengine" ) {
83
     if ($config->get('ENGINE_LINKED')) {
84
         debuglog("Backing up engine configuration...");
85
         print `echo users/backupengine | stash`;
86
     } else {
87
         print "Engine not linked. Not backing up configuration.\n";
88
     }
89
} elsif (lc($arg1) eq "billengine" ) {
90
#     debuglog("Updating billing data on origo.io...");
91
     print `echo users/billengine | stash`;
92
} elsif (lc($arg1) eq "updatenetworkbilling" ) {
93
#     debuglog("Updating billing for all networks...");
94
     updateBillingAllNetworks();
95
} elsif (lc($arg1) eq "updateimagebilling" ) {
96
#     debuglog("Updating billing for all images...");
97
     updateBillingAllImages();
98

    
99
} elsif (lc($arg1) eq "updatedownloads" ) {
100
    print `REMOTE_USER=irigo $base/cgi/images.cgi -a gear_updatedownloads`;
101

    
102
} elsif (lc($arg1) eq "releaseolddhcpleases" ) {
103
    releaseOldDhcpLeases();
104
} elsif (lc($arg1) eq "refreshimages" ) {
105
    print `REMOTE_USER=irigo $base/cgi/images.cgi -a updateregister -f`;
106
} elsif (lc($arg1) eq "updateimagestatus" ) {
107
    # Incoming params: $status $oldstatus $path1 $path2
108
    # Function params: $localpath, $localstatus, $newvirtualsize, $newbackupsize
109
    updateImageStatus($status, $oldstatus, $path1, $path2);
110
} elsif (lc($arg1) eq "updatebackingfile" ) {
111
    # Incoming params: $status $oldstatus $path1 $path2
112
    # Function params: $localpath, $localstatus, $newvirtualsize, $newbackupsize
113
    updateBackingFile($status);
114
} elsif (lc($arg1) eq "unmountallimages" ) {
115
    debuglog("Unmounting all images...");
116
    print "Unmounting all images - hang on...\n";
117
    unmountAllImages();
118
} elsif (lc($arg1) eq "backupallfuel" ) {
119
    debuglog("Backup up fuel for all users...");
120
    print "Backup up fuel for all users, hang on...\n";
121
    backupAllFuel();
122
} elsif (lc($arg1) eq "post-wake") {
123
    debuglog("Running post-wake routines...");
124
    print `REMOTE_USER=irigo $base/cgi/networks.cgi -a gear_restoreall`;
125
} elsif (lc($arg1) eq "post-boot") {
126
    my $baseip;
127
    if ($opentcpports || $openudpports) {
128
        my $basedom = `awk -F'[/:]' '/https:/{print \$4}' /etc/stabile/baseurl`;
129
        chomp $basedom;
130
        if ($basedom =~ /\d+\.\d+\.\d+\.\d+/) {
131
            $baseip = $basedom;
132
        } else {
133
            $baseip = `dig +short \@1.1.1.1 $basedom | tail -n1`;
134
        #    $baseip = `dig +short $basedom | tail -n1`;
135
            chomp $baseip;
136
        }
137
        $baseip = '' if ($baseip =~ /^127/);
138
        print "Hardening access to $baseip\n";
139
    }
140
    if (($opentcpports || $openudpports) && $baseip) {
141
        print `iptables -D INPUT -p icmp --icmp-type 8 -s 0/0 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT 2>/dev/null`;
142
        print `iptables -I INPUT -p icmp --icmp-type 8 -s 0/0 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT`;
143
        print `iptables -D OUTPUT -p icmp --icmp-type 0 -d 0/0 -m state --state ESTABLISHED,RELATED -j ACCEPT 2>/dev/null`;
144
        print `iptables -I OUTPUT -p icmp --icmp-type 0 -d 0/0 -m state --state ESTABLISHED,RELATED -j ACCEPT`;
145
# Don't reply to ICMP timestamp requests
146
        print `iptables -D INPUT -p icmp --icmp-type timestamp-request -j DROP 2>/dev/null`;
147
        print `iptables -I INPUT -p icmp --icmp-type timestamp-request -j DROP 2>/dev/null`;
148
        print `iptables -D OUTPUT -p icmp --icmp-type timestamp-reply -j DROP 2>/dev/null`;
149
        print `iptables -I OUTPUT -p icmp --icmp-type timestamp-reply -j DROP 2>/dev/null`;
150

    
151
        print `iptables -D INPUT -m conntrack -j ACCEPT --ctstate RELATED,ESTABLISHED 2>/dev/null`;
152
        print `iptables -I INPUT -m conntrack -j ACCEPT --ctstate RELATED,ESTABLISHED`;
153
        print `iptables -D INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT 2>/dev/null`;
154
        print `iptables -I INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT`;
155
# Accept tftp requests
156
        print `iptables -D INPUT -p udp -m udp -s 10.0.0.0/8 --dport 69 -j ACCEPT`;
157
        print `iptables -I INPUT -p udp -m udp -s 10.0.0.0/8 --dport 69 -j ACCEPT`;
158
# Accept nfs requests
159
        print `iptables -D INPUT -p udp -m udp -s 10.0.0.0/8 --dport 111 -j ACCEPT`;
160
        print `iptables -I INPUT -p udp -m udp -s 10.0.0.0/8 --dport 111 -j ACCEPT`;
161
        print `iptables -D INPUT -p udp -m udp -s 10.0.0.0/8 --dport 2049 -j ACCEPT`;
162
        print `iptables -I INPUT -p udp -m udp -s 10.0.0.0/8 --dport 2049 -j ACCEPT`;
163
        print `iptables -D INPUT -p udp -m udp -s 10.0.0.0/8 --dport 33333 -j ACCEPT`;
164
        print `iptables -I INPUT -p udp -m udp -s 10.0.0.0/8 --dport 33333 -j ACCEPT`;
165
        print `iptables -D INPUT -p tcp -m tcp -s 10.0.0.0/8 --dport 111 -j ACCEPT`;
166
        print `iptables -I INPUT -p tcp -m tcp -s 10.0.0.0/8 --dport 111 -j ACCEPT`;
167
        print `iptables -D INPUT -p tcp -m tcp -s 10.0.0.0/8 --dport 2049 -j ACCEPT`;
168
        print `iptables -I INPUT -p tcp -m tcp -s 10.0.0.0/8 --dport 2049 -j ACCEPT`;
169
        print `iptables -D INPUT -p tcp -m tcp -s 10.0.0.0/8 --dport 33333 -j ACCEPT`;
170
        print `iptables -I INPUT -p tcp -m tcp -s 10.0.0.0/8 --dport 33333 -j ACCEPT`;
171
# Accept connections from self
172
        print `iptables -D INPUT -s 10.0.0.1 -j ACCEPT 2>/dev/null`;
173
        print `iptables -I INPUT -s 10.0.0.1 -j ACCEPT`;
174
        print `iptables -D INPUT -s 127.0.0.1 -j ACCEPT 2>/dev/null`;
175
        print `iptables -I INPUT -s 127.0.0.1 -j ACCEPT`;
176
# Accept internal connections - access control between subnets is handles by networks.cgi
177
        print `iptables -D INPUT -s 10.0.0.0/8 -d 10.0.0.0/8 -j ACCEPT`;
178
        print `iptables -I INPUT -s 10.0.0.0/8 -d 10.0.0.0/8 -j ACCEPT`;
179
# Disallow all other incoming
180
        print `iptables -D INPUT -j DROP 2>/dev/null`;
181
        print `iptables -A INPUT -j DROP`;
182
    }
183
    if ($opentcpports && $baseip) {
184
        print "Allowing incoming TCP traffic to $baseip for ports $opentcpports\n";
185
        my $ports = join(",",split(/, ?/, $opentcpports));
186
        print `iptables -D INPUT -p tcp -m tcp -m multiport --dports $ports -j ACCEPT 2>/dev/null`;
187
        print `iptables -I INPUT -p tcp -m tcp -m multiport --dports $ports -j ACCEPT`;
188
    }
189
    if ($openudpports && $baseip) {
190
        print "Allowing incoming UDP traffic to $baseip for ports $openudpports\n";
191
        my $ports = join(",",split(/, ?/, $openudpports));
192
        print `iptables -D INPUT -p udp -m udp -m multiport --dports $ports -j ACCEPT 2>/dev/null`;
193
        print `iptables -I INPUT -p udp -m udp -m multiport --dports $ports -j ACCEPT`;
194
    }
195
    if (`pgrep pressurecontrol`) {
196
        debuglog("Running post-boot routines...");
197
        print `REMOTE_USER=irigo $base/cgi/networks.cgi -a gear_restoreall`;
198
        print `REMOTE_USER=irigo $base/cgi/servers.cgi -a gear_autostartall`;
199
        print `REMOTE_USER=irigo $base/cgi/images.cgi -a gear_updatedownloads`;
200
    } else {
201
        debuglog("Not running post-boot routines...");
202
    }
203
} elsif (lc($arg1) eq "pre-shutdown") {
204
    debuglog("Running pre-shutdown routines...");
205
    unmountAllImages();
206
    debuglog("Halting nodes...");
207
    `echo "nodes/haltall" | /usr/local/bin/stash`;
208
    #updateBillingAllNetworks();
209
} elsif (lc($arg1) eq "daily") {
210
    debuglog("Running daily maintenance routine...");
211
    backupAllImages();
212
    updateBillingAllNetworks();
213
    unmountAllImages();
214
} elsif (lc($arg1) eq "restoreallnetworks" ) {
215
    debuglog("Restoring all networks...");
216
    print "Restoring all networks - hang on...\n";
217
    print `REMOTE_USER=irigo $base/cgi/networks.cgi -a gear_restoreall`;
218
} elsif (lc($arg1) eq "showautostart" ) {
219
    print "Showing autostart servers - hang on...\n";
220
    print `REMOTE_USER=irigo $base/cgi/servers.cgi -a showautostart`;
221
} elsif (lc($arg1) eq "autostartservers" ) {
222
    debuglog("Autostarting servers...");
223
    print "Autostarting servers - hang on...\n";
224
    print `REMOTE_USER=irigo $base/cgi/servers.cgi -a autostartall`;
225
} elsif (lc($arg1) eq "resetmonitoring" ) {
226
    debuglog("Resetting monitoring...");
227
    print "Resetting monitoring - hang on...\n";
228
    print `REMOTE_USER=irigo $base/cgi/systems.cgi -a resetmonitoring`;
229
} elsif ($arg1 && $status && $path1) {
230
    debuglog("$arg1 : $status : $oldstatus : $path1 : $path2 : $cmd1 : $cmd2");
231
    if ($status eq "snapshotting") {
232
        # $res .= `/bin/echo "$status" > "$path1.meta"`;
233
        my $macip;
234
        $localpath = $path1;
235
        if ($localpath =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/) {
236
            $macip = $1;
237
            $localpath = $2;
238
            my $esc_localpath = shell_esc_chars($localpath);
239
            $res .= `$sshcmd $macip "sudo /usr/bin/qemu-img snapshot -c snap1 $esc_localpath"`;
240
        } else {
241
            $res .= `/usr/bin/qemu-img snapshot -c snap1 "$path1"`;
242
        }
243
        $dsnap1 = $path2;
244
        $localstatus = $oldstatus;
245
    } elsif ($status eq "unsnapping") {
246
        my $macip;
247
        $localpath = $path1;
248
        if ($localpath =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/) {
249
            $macip = $1;
250
            $localpath = $2;
251
            my $esc_localpath = shell_esc_chars($localpath);
252
            $res .= `$sshcmd $macip "sudo /usr/bin/qemu-img snapshot -d snap1 $esc_localpath"`;
253
        } else {
254
            $res .= `/usr/bin/qemu-img snapshot -d snap1 "$path1"`;
255
        }
256
        $dsnap1 = "--";
257
        $localstatus = $oldstatus;
258
    } elsif ($status eq "reverting") {
259
        my $macip;
260
        $localpath = $path1;
261
        if ($localpath =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/) {
262
            $macip = $1;
263
            $localpath = $2;
264
            my $esc_localpath = shell_esc_chars($localpath);
265
            $res .= `$sshcmd $macip "sudo /usr/bin/qemu-img snapshot -a snap1 $esc_localpath"`;
266
        } else {
267
            $res .= `/usr/bin/qemu-img snapshot -a snap1 "$path1"`;
268
        }
269
        $localstatus = $oldstatus;
270
    } elsif ($status eq "injecting") {
271
        $localpath = $path1;
272
        $localpath2 = $path2;
273
        my $esc_localpath = shell_esc_chars($localpath);
274

    
275
        # Find out if we are dealing with a Windows image
276
        my $xml = `bash -c '/usr/bin/virt-inspector -a "$esc_localpath"'`;
277
        #my $xml = `bash -c '/usr/bin/virt-inspector -a "$esc_localpath"' 2>&1`;
278
        # $res .= $xml . "\n";
279
        my $xmlref;
280
        my $osname;
281
        $xmlref = XMLin($xml) if ($xml =~ /^<\?xml/);
282
        $osname = $xmlref->{operatingsystem}->{name} if ($xmlref);
283
        if ($xmlref && $osname eq 'windows') {
284
            $res .= "We are dealing with a Windows image - trying to fix registry\n";
285
            my $upath = $esc_localpath;
286
            # We need write privileges
287
            $res .= `chmod 666 "$upath"`;
288
            # First try to merge storage registry keys into Windows registry. If not a windows vm it simply fails.
289
            $res .= `bash -c 'cat /usr/share/stabile/mergeide.reg | /usr/bin/virt-win-reg --merge "$upath"' 2>&1`;
290
            # Then try to merge the critical device keys. This has been removed in win8 and 2012, so will simply fail for these.
291
            $res .= `bash -c 'cat /usr/share/stabile/mergeide-CDDB.reg | /usr/bin/virt-win-reg --merge "$upath"' 2>&1`;
292
            if ($res) { debuglog($res); $res = ''; }
293

    
294
            # Try to copy viostor.sys into image
295
            my @winpaths = (
296
                '/Windows/System32/drivers',
297
                '/WINDOWS/system32/drivers/viostor.sys',
298
                '/WINDOWS/System32/drivers/viostor.sys',
299
                '/WINNT/system32/drivers/viostor.sys'
300
            );
301
            foreach my $winpath (@winpaths) {
302
                $res .= "Trying $winpath\n";
303
                my $lscmd = qq|bash -c 'virt-ls -a "$upath" "$winpath"'|;
304
                debuglog("$res"); $res = '';
305
                my $drivers = `$lscmd`;
306
                if ($drivers =~ /viostor/i) {
307
                    $res .= "OK: viostor already installed in $winpath in $upath\n";
308
                    debuglog($res); $res = '';
309
                    syslogit('info', "viostor already installed in $winpath in $upath");
310
                    last;
311
                } elsif ($drivers) {
312
                    my $cmd = qq|bash -c 'guestfish -i -a "$upath" upload /usr/share/stabile/VIOSTOR.SYS $winpath/viostor.sys' 2>&1|;
313
                    my $error = `$cmd`;
314
                    if ($error) {
315
                        $res .= "Error injecting virtio drivers into $upath: $error\n";
316
                        debuglog($res); $res = '';
317
                        syslogit('info', "Error injecting virtio drivers into $upath: $error");
318
                    } else {
319
                        $res .= "Injected virtio drivers into $upath";
320
                        debuglog($res); $res = '';
321
                        syslogit('info', "Injected virtio drivers into $upath");
322
                    }
323
                    last;
324
                } else {
325
                    $res .= "No drivers found in $winpath\n";
326
                    debuglog($res); $res = '';
327
                }
328
            }
329

    
330
        } else {
331
            $res .= "No Windows OS found ($osname) in image, not injecting drivers: $esc_localpath\n";
332
            syslogit('info', "No Windows OS found ($osname) in image, not injecting drivers: $esc_localpath");
333
        }
334
        $localstatus = $oldstatus;
335
    } elsif ($status eq "converting") {
336
        $localpath = $path1;
337
        $localpath2 = $path2;
338
        my $esc_localpath2 = shell_esc_chars($localpath2);
339
        my $upath = $esc_localpath2;
340

    
341
        my $macip;
342
        if ($localpath =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/) {
343
            $macip = $1;
344
            $localpath = $2;
345
            my $esc_localpath = shell_esc_chars($localpath);
346
            my $esc_localpath2 = shell_esc_chars($localpath2);
347
            $res .= `$sshcmd $macip "/usr/bin/qemu-img convert $esc_localpath -O qcow2 $esc_localpath2"`;
348
        } else {
349
            my $mpath;
350
            if (substr($path1,-5) eq '.vmdk' && ( -s (substr($path1,0,-5) . "-flat.vmdk") ) ) {
351
                my $pathname = substr($path1,0,-5);
352
                $mpath = "\"$pathname-flat.vmdk\" ";
353
            # } elsif (substr($path1,-5) eq '.vmdk' && ( -s (substr($path1,0,-5) . "-s001.vmdk")) ) {
354
            #     my $pathname = substr($path1,0,-5);
355
            #     my $i = 1;
356
            #     my $num = '001';
357
            #     while (-e "$pathname-s$num.vmdk") {
358
            #         $mpath .= "\"$pathname-s$num.vmdk\" ";
359
            #         $i++;
360
            #     	$num = substr("00$i", -3);
361
            #     }
362
            } else {
363
                $mpath = "\"$path1\"";
364
            }
365

    
366
            $res .= qq|Firing /usr/bin/qemu-img convert $mpath -O qcow2 "$path2"\n|;
367
            $res .= `/usr/bin/qemu-img convert $mpath -O qcow2 "$path2"`;
368

    
369
            # Setting real UID: $< to UID: $>
370
            # $< = $>;
371

    
372
            # Find out if we are dealing with a Windows image
373
            my $xml = `bash -c '/usr/bin/virt-inspector -a "$esc_localpath2"'`;
374
            # my $xml = `bash -c '/usr/bin/virt-inspector -a "$esc_localpath2"' 2>&1`;
375
            # $res .= $xml . "\n";
376
            my $xmlref;
377
            my $osname;
378
            $xmlref = XMLin($xml) if ($xml =~ /^<\?xml/);
379
            $osname = $xmlref->{operatingsystem}->{name} if ($xmlref);
380
            if ($xmlref && $osname eq 'windows') {
381
                $res .= "We are converting a Windows image - trying to fix registry\n";
382
                # We need write privileges
383
                $res .= `chmod 666 "$upath"`;
384
                # First try to merge storage registry keys into Windows registry. If not a windows vm it simply fails.
385
                $res .= `bash -c 'cat /usr/share/stabile/mergeide.reg | /usr/bin/virt-win-reg --merge "$upath"' 2>&1`;
386
                # Then try to merge the critical device keys. This has been removed in win8 and 2012, so will simply fail for these.
387
                $res .= `bash -c 'cat /usr/share/stabile/mergeide-CDDB.reg | /usr/bin/virt-win-reg --merge "$upath"' 2>&1`;
388
                if ($res) { debuglog($res); $res = ''; }
389

    
390
                # Try to copy viostor.sys into image
391
                my @winpaths = (
392
                    '/Windows/System32/drivers/viostor.sys',
393
                    '/WINDOWS/system32/drivers/viostor.sys',
394
                    '/WINDOWS/System32/drivers/viostor.sys',
395
                    '/WINNT/system32/drivers/viostor.sys'
396
                );
397
                foreach my $winpath (@winpaths) {
398
                    $res .= "Trying $winpath\n";
399
                    my $lscmd = qq|bash -c 'virt-ls -a "$upath" "$winpath"'|;
400
                    debuglog("$res"); $res = '';
401
                    my $drivers = `$lscmd`;
402
                    if ($drivers =~ /viostor/i) {
403
                        $res .= "OK: viostor already installed in $winpath in $upath\n";
404
                        debuglog($res); $res = '';
405
                        syslogit('info', "viostor already installed in $winpath in $upath");
406
                        last;
407
                    } elsif ($drivers) {
408
                        my $cmd = qq|bash -c 'guestfish -i -a "$upath" upload /usr/share/stabile/VIOSTOR.SYS $winpath/viostor.sys' 2>&1|;
409
                        my $error = `$cmd`;
410
                        if ($error) {
411
                            $res .= "Error injecting virtio drivers into $upath: $error\n";
412
                            debuglog($res); $res = '';
413
                            syslogit('info', "Error injecting virtio drivers into $upath: $error");
414
                        } else {
415
                            $res .= "Injected virtio drivers into $upath";
416
                            debuglog($res); $res = '';
417
                            syslogit('info', "Injected virtio drivers into $upath");
418
                        }
419
                        last;
420
                    } else {
421
                        $res .= "No drivers found in $winpath\n";
422
                        debuglog($res); $res = '';
423
                    }
424
                }
425

    
426
            } else {
427
                $res .= "No Windows OS found ($osname) in converted image, not injecting drivers: $esc_localpath2\n";
428
                syslogit('info', "No Windows OS found ($osname) in image, not injecting drivers: $esc_localpath2");
429
            }
430

    
431
            if (-e "$path2") {
432
                syslogit('info', "Converted image to: $path2");
433
            } else {
434
                syslogit('info', "Unable to convert image to: $path2");
435
            }
436
        }
437
        $localstatus = $oldstatus;
438
        $localstatus2 = "unused";
439
    } elsif ($status eq "rebasing") {
440
        $localpath = $path1;
441
        $localpath2 = $path2;
442
        my $macip;
443
        if ($localpath =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/) {
444
            $macip = $1;
445
            $localpath = $2;
446
            my $esc_localpath = shell_esc_chars($localpath);
447
            my $esc_localpath2 = shell_esc_chars($localpath2);
448
            $res .= `$sshcmd $macip "/usr/bin/qemu-img convert $esc_localpath -O qcow2 $esc_localpath2"`;
449
            $res .= `$sshcmd $macip "if [ -f $esc_localpath2 ]; then /bin/mv -v $esc_localpath2 $esc_localpath; fi"`;
450
        } else {
451
            $res .= `/usr/bin/qemu-img convert -O qcow2 "$path1" "$path2"`;
452
            $res .= `if [ -f "$path2" ]; then /bin/mv -v "$path2" "$path1"; fi`;
453
        }
454
        $localstatus = $oldstatus;
455

    
456
        # Release master image if not used by other images
457
        unless (tie %imagereg,'Tie::DBI', {
458
            db=>'mysql:steamregister',
459
            table=>'images',
460
            key=>'path',
461
            autocommit=>0,
462
            CLOBBER=>3,
463
            user=>$dbiuser,
464
            password=>$dbipasswd}) {syslogit('info', "Image register could not be accessed")};
465

    
466
        my $master = ($imagereg{$path1}->{'master'} && $imagereg{$path1}->{'master'} ne '--')?$imagereg{$path1}->{'master'}:'';
467
        my $usedmaster = '';
468
        my @regvalues = values %imagereg;
469
        if ($master) {
470
            foreach my $valref (@regvalues) {
471
                $usedmaster = 1 if ($valref->{'master'} eq $master && $valref->{'path'} ne $path1); # Check if another image is also using this master
472
            }
473
        }
474
        if ($master && !$usedmaster) {
475
            $imagereg{$master}->{'status'} = 'unused';
476
        }
477
        $imagereg{$path1}->{'master'} = '';
478
        syslogit->('info', "Freeing master $master");
479
        untie %imagereg;
480

    
481
    } elsif ($status eq "cloning" || $status eq "copying" || $status eq "vcloning" || $status eq "bcloning") {
482
        $localpath = $path1;
483
        $localpath2 = $path2;
484
        my $macip;
485
        if ($localpath =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/) { # Target is on a node
486
            $macip = $1;
487
            $localpath = $2;
488
            my $esc_localpath = shell_esc_chars($localpath);
489
            my $esc_localpath2 = shell_esc_chars($localpath2);
490

    
491
            $esc_localpath =~ /(.+)\/.*/;
492
            my $sdir = $1;
493
            $esc_localpath2 =~ /(.+)\/.*/;
494
            my $dir = $1;
495

    
496
        # Creating target directory in case it doesn't exist
497
            `$sshcmd $macip /bin/mkdir -p "$dir"`;
498

    
499
        # Mounting remote share on node in case it isn't
500
            for (my $i=0; $i<=$#tenderpathslist; $i++
501
                )
502
            {
503
                my $path = $tenderpathslist[$i];
504
                my $host = $tenderlist[$i];
505
                $host = "10.0.0.1:$path" if ($host eq 'local');
506
                if ($sdir =~ /$path\//) {
507
                    my $cmd = qq/$sshcmd $macip "mountpoint -q $path || sudo mount -o intr,noatime,nfsvers=3 $host $path"/;
508
                    $res .= `$cmd`;
509
                    last;
510
                }
511
            }
512

    
513
            if ($status eq "cloning" || $status eq "bcloning") {
514
                my $cmd = qq|$sshcmd $macip "/usr/bin/qemu-img create -f qcow2 -b $esc_localpath $esc_localpath2 2>\&1"|;
515
                $res .= "$cmd\n";
516
                $res .= `$cmd`;
517
            } elsif ($status eq "copying") {
518
#                $res .= `ssh -l irigo -i /var/www/.ssh/id_rsa_www $macip "/usr/bin/rsync -uv --inplace $esc_localpath $esc_localpath2"`;
519
                $res .= `$sshcmd $macip "/bin/cp -vn $esc_localpath $esc_localpath2"`;
520
            } elsif ($status eq "vcloning") {
521
                $res .= `$sshcmd $macip "/usr/bin/VBoxManage clonehd $esc_localpath $esc_localpath2"`;
522
                $res .= `$sshcmd $macip "/bin/chmod 666 $esc_localpath2"`;
523
            }
524
        } else {
525
            $path2 =~ /(.+)\/.*/;
526
            my $dir = $1;
527
            `/bin/mkdir "$dir"` unless -e $dir;
528
            if ($status eq "cloning" || $status eq "bcloning") {
529
                $res .= `/usr/bin/qemu-img create -f qcow2 -b "$path1" "$path2" 2>\&1`;
530
                debuglog($res) if ($res);
531
            } elsif ($status eq "copying") {
532
#                $res .= `/usr/bin/ionice -c3 /usr/bin/rsync -uv --inplace "$path1" "$path2"`;
533
                $res .= `/usr/bin/ionice -c3 /bin/cp -vn "$path1" "$path2"`;
534
            } elsif ($status eq "vcloning") {
535
                $res .= `/usr/bin/ionice -c3 /usr/bin/VBoxManage clonehd "$path1" "$path2"`;
536
                $res .= `/bin/chmod 666 "$path2"`;
537
            }
538
        }
539
        $newvirtualsize2 = getVirtualSize($path2, $macip); # report size of new image for billing purposes
540
        $localstatus = ($status eq "bcloning" || $status eq "cloning")?"used": $oldstatus;
541
        $localstatus2 = ($status eq "bcloning")?"unused":"unused"; # bcloning = building a system
542

    
543
    } elsif ($status eq "urluploading") {
544
        $localpath = $path1;
545
        $imageurl = $path2;
546
#        $res .= `/bin/dd if=/dev/zero of="$localpath"  bs=1M  count=2; /bin/rm -f "$localpath.meta"`;
547
        $res .= `/usr/bin/wget --no-check-certificate -O "$localpath" "$imageurl"; /bin/rm -f "$localpath.meta"`;
548

    
549
        my $qinfo = `/usr/bin/qemu-img info --force-share "$localpath"`;
550
        $qinfo =~ /virtual size:.*\((.+) bytes\)/g;
551
        $newvirtualsize = int($1);
552
        unless ($newvirtualsize) {
553
            my @stat = stat($localpath);
554
            $newvirtualsize = $stat[7];
555
        }
556
        $localstatus = $oldstatus;
557
    } elsif ($status eq "qcreating") {
558
        $status = "creating";
559
        $localpath = $path1;
560
        # $path2 contains the size in k
561
        my $size = ($path2 / 1024)."M";
562
        my $format = "qcow2";
563
        $format = "vmdk" if ($path1 =~ /\.vmdk$/);
564
        $res .= `/usr/bin/qemu-img create -f $format "$path1" "$size"`;
565
		if(($? >> 8) != 0) { # Report if there was an error creating the image.
566
			syslogit('err', "An error was reported creating the qcow2/raw image.");
567
		}
568
		$res .= `/bin/chmod 666 "$path1"`;
569
		$res .= "$size";
570
		$newvirtualsize = $path2 * 1024;
571
		$localstatus = "unused";
572
    } elsif ($status eq "icreating") {
573
        $status = "creating";
574
        $localpath = $path1;
575
        # $path2 contains the size in k
576
        my $size = ($path2 / 1024)."M";
577
        $res .= `/usr/bin/qemu-img create -f raw "$path1" "$size"`;
578
		$res .= `/bin/chmod 666 "$path1"`;
579
		$newvirtualsize = $path2 * 1024;
580
		$localstatus = "unused";
581
    } elsif ($status eq "vcreating") {
582
        $status = "creating";
583
        $localpath = $path1;
584
        # $path2 contains the size in k
585
        $res .= `/usr/bin/VBoxManage createhd --filename "$path1" --size "$path2" --format VDI`;
586
		$res .= `/bin/chmod 666 "$path1"`;
587
		$newvirtualsize = $path2 * 1024;
588
		$localstatus = "unused";
589
    } elsif ($status eq "resizing") {
590
        $localpath = $path1;
591
        # $path2 contains the size in k
592
        my $size = int($path2 / 1024)."M";
593
        my $macip;
594
        if ($localpath =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/) { # We are dealing with an image on a node
595
            $macip = $1;
596
            $localpath = $2;
597
            my $esc_localpath = shell_esc_chars($localpath);
598
            my $cmd = qq|$sshcmd $macip "sudo /usr/bin/qemu-img resize $esc_localpath $size" 2>&1|;
599
            $res .= "COMMAND: $cmd\n";
600
            $res .= `$cmd`;
601
        } else {
602
            $res .= `/usr/bin/qemu-img resize "$path1" "$size"`;
603
        }
604
        $newvirtualsize = $path2 * 1024;
605
        $localstatus = $oldstatus;
606
    } elsif ($status eq "moving") {
607
    # On node
608
        if ($path2 =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/ && $path1 =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/) {
609
            my $macip = $1;
610
            $localpath = $2;
611
            $path2 =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/;
612
            $localpath2 = $2;
613

    
614
            my $esc_path1 = shell_esc_chars($path1);
615
            my $esc_path2 = shell_esc_chars($path2);
616
            my $esc_localpath = shell_esc_chars($localpath);
617
            my $esc_localpath2 = shell_esc_chars($localpath2);
618

    
619
            $newvirtualsize = getVirtualSize($esc_localpath, $macip);
620
            $newvirtualsize2 = 0;
621

    
622
            $localpath2 =~ /(.+)\/.+/;
623
            my $localdir2 = $1;
624
            $res .= qq[Moving: $sshcmd $macip /bin/mv -n "$localpath" "$localpath2"\n];
625
            $res .= `$sshcmd $macip /bin/mv -n "$localpath" "$localpath2"`;
626

    
627
            $localstatus2 = $oldstatus;
628
            $localstatus = "";
629

    
630
    # To node
631
        } elsif ($path2 =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/) {
632
        # Update billing
633
            $newvirtualsize = getVirtualSize($path1);
634
            $newvirtualsize2 = 0;
635
            my $macip = $1;
636
            $localpath = $2;
637
            $localpath2 = $path1;
638
        #    my $md5sum;
639
        #    my $md5sum2;
640

    
641
            my $esc_path1 = shell_esc_chars($path1);
642
            my $esc_localpath = shell_esc_chars($localpath);
643
            my $esc_path2 = "$macip:$esc_localpath";
644

    
645
            $res .= qq[Moving: /usr/bin/rsync -vW --sparse -e "$sshcmd" $esc_path1 "$esc_path2"\n];
646
        #    $md5sum = (split(" ", `/usr/bin/md5sum "$path1"`))[0];
647
        #    $res .= "MD5: $md5sum\n";
648
            my $mvres = system(qq|/usr/bin/rsync -vW --sparse -e "$sshcmd" $esc_path1 "$esc_path2"|);
649
            my $chres .= `$sshcmd $macip "/bin/chmod 666 $esc_localpath"`;
650
        #    $md5sum2 = (split(" ", `$sshcmd $macip "/usr/bin/md5sum -b $esc_localpath"`))[0];
651
        #    $res .= "MD5: $md5sum2\n";
652
        #    if ($md5sum && ($md5sum eq $md5sum2)) {
653
            unless ($mvres || $chres) { # Sanity check
654
                $localstatus = $oldstatus;
655
                $localstatus2 = "unused";
656
                unlink $path1;
657
                $localstatus2 = "removed"; # The image being left behind is removed from db after moving to new location
658
            }
659
    # From node
660
        } elsif ($path1 =~ m/(\d+\.\d+\.\d+\.\d+)\:(\/.+)/) {
661
            my $macip = $1;
662
            $localpath2 = $2;
663
            $localpath = $path2;
664
        #    my $md5sum;
665
        #    my $md5sum2;
666
            my $esc_localpath = shell_esc_chars($localpath);
667
            my $esc_localpath2 = shell_esc_chars($localpath2);
668
            my $esc_path1 = "$macip:$esc_localpath2";
669
            my $esc_path2 = shell_esc_chars($path2);
670
        #    $md5sum = (split(" ", `$sshcmd $macip "/usr/bin/md5sum -b $esc_localpath2"`))[0];
671
        #    $res .= "MD5: $md5sum\n";
672
            my $mvres = system(qq|/usr/bin/rsync -vW --sparse -e "$sshcmd" "$esc_path1" $esc_path2|);
673
        #    $md5sum2 = (split(" ", `/usr/bin/md5sum "$path2"`))[0];
674
        #    $res .= "MD5: $md5sum2\n";
675
        #    if ($md5sum && ($md5sum eq $md5sum2)) {
676
            unless ($mvres) { # Sanity check
677
                $localstatus = $oldstatus;
678
                $localstatus2 = "unused";
679
                `$sshcmd $macip "/usr/bin/unlink $esc_localpath2"`;
680
                $localstatus2 = "removed"; # The image being left behind is removed from db after moving to new location
681
            }
682
        # Update billing
683
            $newvirtualsize = getVirtualSize($path2);
684
            $newvirtualsize2 = 0;
685
        } else {
686
            $localpath = $path2;
687
            $localpath2 = $path1;
688
            $res .= `/bin/mv -v "$path1" "$path2"`;
689
            $res .= `/bin/mv -v "$path1.meta" "$path2.meta"` if (-e "$path1.meta");
690
            $localstatus2 = "removed";
691
            $localstatus = $oldstatus;
692
        # Update billing
693
            $newvirtualsize = getVirtualSize($path2);
694
            $newvirtualsize2 = 0;
695
        }
696
    } elsif ($status eq "backingup" || $status eq "lbackingup") {
697
        $user = $arg1;
698
        # $path1 is the image to back up (including potential subdir), $path2 the source dir (storage pool) and $cmd1 the target dir (general backup dir)
699
        $localpath = "$path2/$user/$path1";
700
        mkdir "$cmd1/$user" unless -d "$cmd1/$user"; # Create the target dirs which will contain the backup
701
        my $pool = $path2;
702
        my $image = $path1;
703
        my $subdir; # 1 level of subdirs supported
704
        if ($path1 =~ /(.+)\/(.+)/) {
705
            $subdir = $1;
706
            $image = $2;
707
        }
708
        if ($subdir) {
709
            mkdir "$cmd1/$user/$subdir" unless -d "$cmd1/$user/$subdir";
710
            mkdir "$cmd1/$user/$subdir/$image" unless -d "$cmd1/$user/$subdir/$image";
711
        } else {
712
            mkdir "$cmd1/$user/$image" unless -d "$cmd1/$user/$image";
713
        }
714

    
715
        if (-d "/mnt/$user-$image") {
716
            $res .= "Image is already being backed up";
717
        } else {
718
            my $snapname;
719
            my $snappath;
720
            my $snapsrcdir;
721
            my $lvolgroup;
722
            if ($status eq "lbackingup") { # Do a local lvm snapshot before backing up
723
                `/sbin/modprobe dm-snapshot`; # Make sure we can make lvm snapshots
724
                $snapname = "$user-$image";
725
                $snapname =~ tr/ /-/; #No spaces allowed in snapshot names...
726
                $snapname =~ tr/@/_/; #No funny chars allowed in snapshot names...
727
                $snappath = "/mnt/$snapname"; # The path to mount our snapshot on
728
                mkdir $snappath;
729

    
730
                my $q = `/bin/cat /proc/mounts | grep "$pool"`; # Find the lvm volume mounted on /mnt/images
731
                ($q =~ m/\/dev\/mapper\/(\S+)-(\S+) $pool.+/g)[-1]; # Select last match
732
                $lvolgroup = $1;
733
                my $lvol = $2;
734

    
735
                $res .= `/sbin/lvcreate -L1024M -s -n $snapname /dev/$lvolgroup/$lvol`; # Take a snapshot
736

    
737
                debuglog("$oldstatus $pool /dev/$lvolgroup/$lvol $snapname \"$snappath\"");
738

    
739
                $res .= changeFstab($snapname, $pool); # Change fstab to allow mount
740
                $res .= `/bin/mount "$snappath" 2>&1`; # Mount the snapshot
741
                $res .= `ls -l $snappath/$user` . "\n";
742
                $snapsrcdir = "$snappath/$user"; # Change source dir to our new snapshot
743
            } else {
744
                $snapsrcdir = "$pool/$user";
745
            }
746

    
747
            # Do the backup
748
            $res .= `/usr/bin/rdiff-backup --print-statistics --include "$snapsrcdir/$path1" --exclude '**' "$snapsrcdir" "$cmd1/$user/$path1"`;
749
            $res .= `/usr/bin/rdiff-backup --print-statistics --force --remove-older-than $cmd2  "$cmd1/$user/$path1"` if ($cmd2);
750
            `/bin/chown -R irigo:irigo "$cmd1/$user/$path1"`;
751
            # Clean up
752
            if ($status eq "lbackingup") {
753
                $res .= `/bin/umount "$snappath"`;
754
                $res .= changeFstab($snapname, $pool, 1);
755
                $res .= `/bin/rm -r "$snappath"` unless (-d "$snappath/$user"); # Sanity check - don't delete mount dir if snapshot still mounted
756
                debuglog("removing logical volume /dev/$lvolgroup/$snapname");
757
                $res .= `/sbin/lvremove -f /dev/$lvolgroup/$snapname`;
758
            }
759

    
760
           if (-d "$cmd1/$user/$path1") { # Report new backup size for billing
761
               if ($subdir) {
762
                   $newbackupsize = getBackupSize("/$subdir", $image, $user);
763
               } else {
764
                   $newbackupsize = getBackupSize('', $image, $user);
765
               }
766
           }
767
        # Update btime
768
            my $buser = $user;
769
            $buser = 'irigo' if ($user eq 'common');
770
            my $bcmd = "REMOTE_USER=$buser $base/cgi/images.cgi -a updatebtime -i " . uri_escape($localpath);
771
            my $scmd = "/usr/bin/$sshcmd 127.0.0.1";
772
            $res .= "$scmd $bcmd\n";
773
            $res .= `$scmd $bcmd`;
774
            $res .= "Updating btime: for $localpath\n\n";
775

    
776
            my $bmes;
777
            if ($status eq "backingup" || $status eq "lbackingup") {
778
                $bmes;
779
                if ($res =~ /TotalDestinationSizeChange (\d)(.+\))/) {
780
                    if ($1 eq "0") {
781
                        $bmes = "No changes to back up";
782
                    } else {
783
                        $bmes = "Backed up $1$2";
784
                        #    $bmes .= " in $1$2" if ($res =~ /ElapsedTime (\d)(.+\))/);
785
                    }
786
                } elsif ($res =~ /(Image is already being backed up)/) {
787
                    $bmes = $1;
788
                } else {
789
                    my $hres = $res;
790
                    $hres =~ s/\n/<br>/g;
791
                    $hres =~ s/\"/\\"/g;
792
                    $bmes = "Backup failed: $hres";
793
                }
794
                $uimsg = $bmes;
795
            }
796

    
797
        }
798
        $localstatus = $oldstatus;
799

    
800
    } elsif ($status eq "frestoring") {
801
        $user = $arg1;
802
        my($bname, $dirpath, $suffix) = fileparse($path1, (".vmdk", ".img", ".vhd", ".qcow", ".qcow2", ".vdi", ".iso"));
803
        my $mountpath = "$dirpath.$bname$suffix";
804
        my $mounts = `/bin/cat /proc/mounts`;
805
        my $mmounts = `/bin/df`;
806
        my $mounted = ($mounts =~ /$mountpath/ && $mmounts =~ /$mountpath/);
807
        my $restorepath = "$dirpath$bname.iso";
808
        if (-e $restorepath) {
809
            my $i = 1;
810
            while (-e "$dirpath$bname.$i.iso") {$i++;}
811
            $restorepath = "$dirpath$bname.$i.iso";
812
        }
813
        if ($mounted) {
814
            $res .= "Restoring files to: /tmp/restore/$user/$bname$suffix -> $restorepath\n";
815
            $res .= `/bin/echo $status > "$restorepath.meta"`;
816

    
817
            `/bin/mkdir -p "/tmp/restore/$user/$bname$suffix"` unless (-e "/tmp/restore/$user/$bname$suffix");
818
            my @files = split(/:/, uri_unescape($path2));
819
            foreach $f (@files) {
820
                if (-e "$mountpath$f" && chdir($mountpath)) {
821
                    $f = substr($f,1) if ($f =~ /^\//);
822
                    eval {`/usr/bin/rsync -aR --sparse "$f" /tmp/restore/$user/$bname$suffix`; 1;}
823
                        or do {$e=1; $res .= "ERROR Problem restoring files $@\n";};
824
                } else {
825
                    $res .= "ERROR $f not found in $mountpath\n";
826
                }
827
            }
828
            if (chdir "/tmp/restore/$user/$bname$suffix") {
829
                eval {$res .= `/usr/bin/genisoimage -o "$restorepath" -iso-level 4 .`; 1;}
830
                    or do {$e=1; $res .= "Stream=ERROR Problem restoring files $@\n";};
831
                $res .= `/bin/rm -rf /tmp/restore/$user/$bname$suffix`;
832
                $res .= "OK Restored files from /tmp/restore/$user/$bname$suffix to $restorepath\n";
833
            } else {
834
                $res .= "ERROR Unable to chdir to /tmp/restore/$user/$bname$suffix\n";
835
            }
836

    
837
            #$res .= `/bin/fusermount -u "$mountpath"`;
838
            #$res .= `/bin/umount "$mountpath"`;
839
            #my $bcmd = "REMOTE_USER=$user $base/cgi/images.cgi " . $postdata;
840
            #$res .= "Unmounting $path1 " . `$bcmd` . "\n";
841

    
842
            $localpath = $path1;
843
            $localstatus = $oldstatus;
844
            $localpath2 = $restorepath;
845
            $localstatus2 = "unused";
846
        # Update billing
847
            $newvirtualsize = getVirtualSize($restorepath);
848
            unlink "$restorepath.meta";
849

    
850
            my $postdata = "-a unmount -i " . uri_escape($path1);
851
            $res .= "Unmounted $path1 " . `REMOTE_USER=$user $base/cgi/images.cgi $postdata`;
852
    #        `/usr/bin/$sshcmd 127.0.0.1 REMOTE_USER=$user $base/cgi/images.cgi $postdata`;
853
        } else {
854
            $res .= "ERROR You must mount image on $mountpath before restoring\n";
855
        }
856

    
857
    } elsif ($status eq "restoring") {
858
        $user = $arg1;
859
        # $path1 is the image path, $path2 backup dir, $cmd1 the increment, $cmd2 is the restore path
860
        my $subdir; # 1 level of subdirs supported
861
        if ($path1 =~ /\/$user\/(.+\/)/) {
862
            $subdir = $1;
863
        }
864
        my($bname, $dirpath, $suffix) = fileparse($path1, (".vmdk", ".img", ".vhd", ".qcow", ".qcow2", ".vdi", ".iso"));
865
        my $incfile;
866
        my $restorepath = $cmd2;
867

    
868
        if ($cmd1 eq "mirror") {
869
            my $mir = `/bin/ls "$path2/$user/$subdir$bname$suffix/rdiff-backup-data" | grep current_mirror`;
870
            if ($mir =~ /current_mirror\.(\S+)\.data/) {
871
                $incfile = "$path2/$user/$subdir$bname$suffix/$bname$suffix";
872
            }
873
        } else {
874
            if ($cmd1 =~ /^SNAPSHOT-/) { # Z-restore
875
                $incfile = "$path2/$user/$subdir$bname$suffix";
876
            } else { # Not a Z-restore
877
                $incfile = "$path2/$user/$subdir$bname$suffix/rdiff-backup-data/increments/$bname$suffix.$cmd1.diff.gz";
878
            }
879
        }
880

    
881
        $res .= `/bin/echo $status > "$restorepath.meta"`;
882

    
883
        $res .= "Restoring: $incfile -> $restorepath ";
884
        if ($cmd1 eq "mirror" || $cmd1 =~ /^SNAPSHOT-/) {
885
            $res .= `/usr/bin/ionice -c3 /bin/cp -vn "$incfile" "$restorepath"`;
886
        } else {
887
            $res .= `/usr/bin/rdiff-backup "$incfile" "$restorepath"`;
888
        }
889

    
890
        $localpath = $path1;
891
        $localstatus = $oldstatus;
892
        $localpath2 = $restorepath;
893
        $localstatus2 = 'unused';
894
        $res .= `/bin/rm -f "$restorepath.meta"`;
895
    # Update billing
896
        $newvirtualsize = getVirtualSize($restorepath);
897
        unlink "$restorepath.meta";
898
    } else {
899
        debuglog("$arg1 : $status : $oldstatus : $path1 : $path2 : $cmd1 : $cmd2 : $res");
900
        print "No action $status\n";
901
    }
902

    
903
    sleep 1;
904
    if ($localpath) {
905
        debuglog("done: $arg1 : $localstatus : $localpath");
906
        updateImageStatus($localpath, $localstatus, $newvirtualsize, $newbackupsize);
907
    }
908
    if ($localpath2) {
909
        debuglog("done2: $arg1 : $localstatus2 : $localpath2");
910
        updateImageStatus($localpath2, $localstatus2, $newvirtualsize2, $newbackupsize2);
911
    }
912

    
913
#    updateClientUI($arg1, $localpath, $localstatus, $dsnap1);
914
    my $updateobj = {user=>$arg1, tab=>"images", path=>$localpath, status=>$localstatus, snap1=>$dsnap1, type=>"update"};
915
    $updateobj->{'message'} = $uimsg if ($uimsg);
916
    my $uires = $main::updateUI->($updateobj);
917
#    debuglog("Updating UI with user=>$arg1 path=>$localpath, status=>$localstatus, $uires");
918

    
919
    if ($localpath2 && ($status =~ /cloning|copying|converting|moving|restoring/)) {
920
#        updateClientUI($arg1, $localpath2, $localstatus2);
921
        $uires = $main::updateUI->({user=>$arg1, tab=>"images", path=>$localpath2, status=>$localstatus, type=>"update"});
922
#        debuglog("Updating UI with user=>$arg1 path=>$localpath2, status=>$localstatus2, $uires");
923
    }
924

    
925
    print $res if ($res);
926
    debuglog($res) if ($res);
927
} else {
928
    print "No valid input...\n";
929
}
930

    
931
exit 0;
932

    
933
sub updateClientUI {
934
    return; # obsolete
935
    my ($username, $dpath, $dstatus, $snap1) = @_;
936
    if ($username) {
937

    
938
        unless (tie %imagereg,'Tie::DBI', {
939
            db=>'mysql:steamregister',
940
            table=>'images',
941
            key=>'path',
942
            autocommit=>0,
943
            CLOBBER=>3,
944
            user=>$dbiuser,
945
            password=>$dbipasswd}) {syslogit('info', "Image register could not be accessed")};
946

    
947
        my $duuid;
948
        my $dname;
949
        if ($dpath && $imagereg{$dpath}) {
950
            $duuid = $imagereg{$dpath}->{'uuid'} ;
951
            $dname = $imagereg{$dpath}->{'name'};
952
        }
953
        untie %imagereg;
954
        #$duuid = $dpath unless ($duuid);
955
        #$duuid = 'none' unless ($duuid);
956

    
957
        my $bmes;
958
        if ($status eq "backingup" || $status eq "lbackingup") {
959
            $bmes;
960
            if ($res =~ /TotalDestinationSizeChange (\d)(.+\))/) {
961
                if ($1 eq "0") {
962
                    $bmes = "No changes to back up ($dname)";
963
                } else {
964
                    $bmes = "Backed up $1$2";
965
                #    $bmes .= " in $1$2" if ($res =~ /ElapsedTime (\d)(.+\))/);
966
                }
967
            } elsif ($res =~ /(Image is already being backed up)/) {
968
                $bmes = $1;
969
            } else {
970
                my $hres = $res;
971
                $hres =~ s/\n/<br>/g;
972
                $hres =~ s/\"/\\"/g;
973
                $bmess = "Backup failed: $hres";
974
            }
975
        }
976
        syslogit('info', "$bmes: $path1") if ($bmes);
977
        my $newtasks = "{\"type\":\"update\",\"tab\":\"images\",\"timestamp\":$current_time" .
978
        ($duuid?",\"uuid\":\"$duuid\"":"") .
979
        ($dstatus?",\"status\":\"$dstatus\"":"") .
980
        ($snap1?",\"snap1\":\"$snap1\"":"") .
981
        ($status eq "backingup"?",\"backup\":\"$path1\"":"") .
982
        ($bmes?",\"message\":\"$bmes\", \"backup\":\"$path1\"":"") .
983
        ",\"sender\":\"steamExec\"" .
984
        "}, ";
985

    
986
        opendir my($dh), '/tmp' or die "Couldn't open '/tmp': $!";
987
        my @files;
988
        if ($username eq 'common') {
989
            # write tasks to all admin user's session task pipes
990
            @files = grep { /.*~A-.*\.tasks$/ } readdir $dh;
991
        } else {
992
            # write tasks to all the user's session task pipes
993
            @files = grep { /^$username~.*\.tasks$/ } readdir $dh;
994
        }
995
        closedir $dh;
996
        my @pfiles;
997
        foreach my $f (@files) {
998
            push @pfiles, "/tmp/$f" if (`pgrep -f "$f"`); # Only include pipes with active listeners
999
        };
1000
        my $tasksfiles = join(' ', @pfiles);
1001
        $tasksfiles = $1 if ($tasksfiles =~ /(.+)/); #untaint
1002
        # Write to users named pipes if user is logged in
1003
        if ($tasksfiles) {
1004
            $res = `/bin/echo \'$newtasks\' | /usr/bin/tee  $tasksfiles \&`;
1005
        }
1006
    }
1007
}
1008

    
1009
sub changeFstab {
1010
	my $image = $_[0];
1011
	my $pool = $_[1];
1012
	my $remove = $_[2];
1013
	return 0 unless ($image);
1014
	return 0 unless (index($image, " ")==-1);
1015
	copy($fstab, "$fstab.steam.bak") or return 0;
1016

    
1017
	my $q = `/bin/cat /proc/mounts | grep "$pool"`; # Find the lvm volume mounted on /mnt/images
1018
    # $q =~ /\/dev\/mapper\/(\S+)-(\S+) $pool.+/;
1019
    ($q =~ m/\/dev\/mapper\/(\S+)-(\S+) $pool.+/g)[-1]; # Select last match
1020
    my $lvolgroup = $1;
1021
    my $lvol = $2;
1022

    
1023
	my $newfile = "";
1024
	my $match;
1025
	open (FILE, $fstab);
1026
	while (<FILE>) {
1027
		chomp;
1028
		my $line = $_;
1029
		if ($line =~ /^\/dev\/$lvolgroup\/$image/) {
1030
			$newfile .= "$line\n" unless ($remove);
1031
			$match = 1;
1032
		} else {
1033
			$newfile .= "$line\n";
1034
		}
1035
	}
1036
    $newfile .= "/dev/$lvolgroup/$image /mnt/$image ext4 users,ro 0 0\n" unless ($remove || $match);
1037
	close (FILE);
1038
	open( FILE2, ">$fstab" );
1039
	#open( FILE2, ">/tmp/fstab.new" );
1040
	print FILE2 $newfile;
1041
	close(FILE2);
1042
	#copy("/tmp/fstab.new", $fstab) or return 0;
1043
	return "$fstab updated with /dev/$lvolgroup/$image /mnt/$image $match : $remove\n";
1044
}
1045

    
1046
sub updateBackingFile {
1047
    my $imagepath = $_[0];
1048
    unless (-e $imagepath) {
1049
        print "Not updating backing file for image (not found) $imagepath\n";
1050
        return;
1051
    }
1052
    $imagepath = uri_escape($imagepath);
1053
    my $bcmd = qq|REMOTE_USER=irigo $base/cgi/images.cgi -a updatebackingfile -i "$imagepath"|;
1054
    print `$bcmd`;
1055
}
1056

    
1057
sub updateImageStatus {
1058
	my $imagepath = $_[0];
1059
	my $imagestatus = $_[1];
1060
	my $newvsize = $_[2]; # Size of image has changed
1061
	my $newbsize = $_[3]; # Size of image's backup has changed
1062
	return unless ($imagepath);
1063
    # if (-e $imagepath) {
1064
    #     print "Updating image $imagepath, $imagestatus, $newvsize, $newbsize\n";
1065
    # } else {
1066
    #     print "Not updating image (not found) $imagepath, $imagestatus, $newvsize, $newbsize\n";
1067
    #     return;
1068
    # }
1069

    
1070
    unless (tie %imagereg,'Tie::DBI', {
1071
        db=>'mysql:steamregister',
1072
        table=>'images',
1073
        key=>'path',
1074
        autocommit=>0,
1075
        CLOBBER=>3,
1076
        user=>$dbiuser,
1077
        password=>$dbipasswd}) {syslogit('info', "Image register could not be accessed")};
1078

    
1079
    my $imageuser = $arg1;
1080
    my $imagename;
1081
    if (($imagestatus eq 'uploading' || $imagestatus eq 'downloading') && !$imagereg{$imagepath}) {
1082
        # An image is being uploaded
1083
        my $ug = new Data::UUID;
1084
        my $newuuid = $ug->create_str();
1085
        $imagename = $imagepath;
1086
        if ($imagename =~ /.+\/(.+)\/(.+)\.(.+)/) {
1087
            $imageuser = $1;
1088
            $imagename = $2;
1089
            my $imagetype = $3 || 'qcow2';
1090
            $imagereg{$imagepath} = {
1091
                uuid => $newuuid,
1092
                path => $imagepath,
1093
                name => $imagename,
1094
                user => $imageuser,
1095
                type => $imagetype,
1096
                virtualsize => $newvsize,
1097
                size => $newvsize,
1098
                status => $imagestatus
1099
            };
1100
            $arg1 = $imageuser;
1101
        }
1102
    } elsif ($imagestatus ne 'removed') {
1103
        if ($imagestatus eq 'installable') {
1104
            $imagestatus = 'unused';
1105
            $imagename = $imagereg{$imagepath}->{'name'} if ($imagereg{$imagepath});
1106
            $imagereg{$imagepath}->{'installable'} = 'true';
1107
            $main::updateUI->(
1108
                {user=>"irigo", tab=>"images", path=>"$imagepath", status=>"$imagestatus", message=>"Image $imagename downloaded and ready to install", type=>"update"}
1109
            );
1110
            $arg1 = 'irigo';
1111
            syslogit('info', "Downloaded $imagepath from registry");
1112
        } elsif ($imagestatus eq 'downloaded') {
1113
            $imagestatus = 'unused';
1114
            $imagename = $imagereg{$imagepath}->{'name'} if ($imagereg{$imagepath});
1115
            $main::updateUI->(
1116
                {user=>"irigo", tab=>"images", path=>"$imagepath", status=>"$imagestatus", message=>"Image $imagename downloaded", type=>"update"}
1117
            );
1118
            $arg1 = 'irigo';
1119
            syslogit('info', "Downloaded $imagepath from registry");
1120
        }
1121
        $imagereg{$imagepath}->{'status'} = $imagestatus;
1122
        $imagereg{$imagepath}->{'status'} = $imagestatus;
1123
        $imagereg{$imagepath}->{'virtualsize'} = $newvsize if ($newvsize);
1124
    } else {
1125
        syslogit('info', "Deleting $imagepath from DB ($imagestatus)");
1126
        delete $imagereg{$imagepath};
1127
    }
1128
    tied(%imagereg)->commit;
1129
    untie %imagereg;
1130
    if ((defined $newvsize) || (defined $newbsize)) {
1131
        updateBilling($arg1) if ($arg1 !~ /\//);
1132
    }
1133
}
1134

    
1135
sub updateBillingAllNetworks {
1136
    my $t = new Proc::ProcessTable;
1137
    my $i = 0;
1138
    foreach $p ( @{$t->table} ){
1139
        my $pcmd = $p->cmndline;
1140
        if ($pcmd =~ /(.*steamExec updatenetworkbilling)/) { # || $pcmd =~ /(.*apache2 -k start)/) {
1141
            $i++;
1142
        }
1143
    }
1144
    if ($i>1) {
1145
        print "Already updating network billing! ($i)\n";
1146
        return;
1147
    }
1148
    print "Updating network billing for all users...\n";
1149

    
1150
    unless (tie %userreg,'Tie::DBI', {
1151
        db=>'mysql:steamregister',
1152
        table=>'users',
1153
        key=>'username',
1154
        autocommit=>0,
1155
        CLOBBER=>1,
1156
        user=>$dbiuser,
1157
        password=>$dbipasswd}) {return 0};
1158

    
1159
    my @regvalues = values %userreg;
1160
    foreach my $valref (@regvalues) {
1161
        my $buser = $valref->{'username'};
1162
        next if ($buser eq 'guest');
1163
        print "Updating billing for $buser\n";
1164
        my $bcmd = "REMOTE_USER=$buser $base/cgi/networks.cgi -a updatebilling";
1165
    # Actually do the update
1166
        print `$bcmd`;
1167

    
1168
    }
1169
    untie %userreg;
1170
}
1171

    
1172
sub updateBillingAllImages {
1173
    my $t = new Proc::ProcessTable;
1174
    my $i = 0;
1175
    foreach $p ( @{$t->table} ){
1176
        my $pcmd = $p->cmndline;
1177
        if ($pcmd =~ /(.*steamExec updateimagebilling)/) { # || $pcmd =~ /(.*apache2 -k start)/) {
1178
            $i++;
1179
        }
1180
    }
1181
    if ($i>1) {
1182
        print "Already updating image billing! ($i)\n";
1183
        return;
1184
    }
1185
    print "Updating image billing for all users...\n";
1186

    
1187
    unless (tie %userreg,'Tie::DBI', {
1188
        db=>'mysql:steamregister',
1189
        table=>'users',
1190
        key=>'username',
1191
        autocommit=>0,
1192
        CLOBBER=>1,
1193
        user=>$dbiuser,
1194
        password=>$dbipasswd}) {return 0};
1195

    
1196
    my @regvalues = values %userreg;
1197
    foreach my $valref (@regvalues) {
1198
        my $buser = $valref->{'username'};
1199
        next if ($buser eq 'guest');
1200
        print "Updating billing for $buser\n";
1201
        my $bcmd = "REMOTE_USER=$buser $base/cgi/images.cgi -a updatebilling";
1202
    # Actually do the update
1203
        print `$bcmd`;
1204
    }
1205
    untie %userreg;
1206
}
1207

    
1208
sub markMonitoredServers {
1209
    print "Marking monitored servers in /var/log/stabile...\n";
1210

    
1211
}
1212

    
1213
sub releaseOldDhcpLeases {
1214
    print "Releasing DHCP leases logged as old leases in syslog...\n";
1215
    my $datanic = $config->get('ENGINE_DATA_NIC');
1216
	unless (tie %networkreg,'Tie::DBI', {
1217
		db=>'mysql:steamregister',
1218
		table=>'networks',
1219
		key=>'uuid',
1220
		autocommit=>0,
1221
		CLOBBER=>3,
1222
		user=>$dbiuser,
1223
		password=>$dbipasswd}) {throw Error::Simple("Stroke=Error Register could not be accessed")};
1224
    my %leases;
1225
    $cmd = qq[tail -n 500 /var/log/syslog | grep -oP " not using configured address .+ because it is leased to .+\$" | sort |  uniq];
1226
    my @loglines = split("\n", `$cmd`);
1227
    foreach my $line (@loglines) {
1228
        $line =~ /not using configured address (.+) because it is leased to (\S+)/;
1229
        my $relip = $1;
1230
        my $relmac = $2;
1231
        $leases{$relip} = {
1232
            ip=>$relip,
1233
            mac=>$relmac
1234
        };
1235
        my $relid;
1236
        if ($relip =~ /10\.(\d+)\.(\d+)\.\d+/) {
1237
            $relid = (0 + "$1$2");
1238
        } else {
1239
            foreach my $net (values %networkreg) {
1240
                if ($net->{'externalip'} eq $relip) {
1241
                    $relid = $net->{'id'};
1242
                    last
1243
                }
1244
            }
1245
        }
1246
        print "Releasing br$relid $relip $relmac\n";
1247
        # print `/usr/bin/dhcp_release $datanic.$relid $relip $relmac`;
1248
        print `/usr/bin/dhcp_release br$relid $relip $relmac`;
1249
        print `perl -i -ne 'print if !/$relip/' /var/lib/misc/dnsmasq.leases`;
1250
        # print `pkill -HUP -f interface=$datanic.$relid`;
1251
        print `pkill -HUP -f "interface=br$relid"`;
1252
    }
1253
    untie %networkreg;
1254
}
1255

    
1256
sub backupAllFuel {
1257
    unless (tie %userreg,'Tie::DBI', {
1258
        db=>'mysql:steamregister',
1259
        table=>'users',
1260
        key=>'username',
1261
        autocommit=>0,
1262
        CLOBBER=>1,
1263
        user=>$dbiuser,
1264
        password=>$dbipasswd}) {return 0};
1265

    
1266
    my @regvalues = values %userreg;
1267
    foreach my $valref (@regvalues) {
1268
        my $buser = $valref->{'username'};
1269
        my $privileges = $valref->{'privileges'};
1270
        unless (index($privileges,"d")!=-1) {
1271
            my $bcmd = "REMOTE_USER=$buser $base/cgi/images.cgi -a backupfuel";
1272
            print `$bcmd`;
1273
        }
1274
    }
1275
    untie %userreg;
1276
}
1277

    
1278

    
1279
sub unmountAllImages {
1280
    unless (tie %userreg,'Tie::DBI', {
1281
        db=>'mysql:steamregister',
1282
        table=>'users',
1283
        key=>'username',
1284
        autocommit=>0,
1285
        CLOBBER=>1,
1286
        user=>$dbiuser,
1287
        password=>$dbipasswd}) {return 0};
1288

    
1289
    my @regvalues = values %userreg;
1290
    foreach my $valref (@regvalues) {
1291
        my $buser = $valref->{'username'};
1292
        my $privileges = $valref->{'privileges'};
1293
        my $bcmd = "REMOTE_USER=$buser $base/cgi/images.cgi -a unmountall" unless (index($privileges,"d")!=-1);
1294
    # Actually do the update
1295
        print `$bcmd`;
1296
    }
1297
    untie %userreg;
1298
}
1299

    
1300
sub backupAllImages {
1301
    my $t = new Proc::ProcessTable;
1302
    my $i = 0;
1303
    foreach $p ( @{$t->table} ){
1304
        my $pcmd = $p->cmndline;
1305
        if ($pcmd =~ /(.*steamExec backupallimages)/) { # || $pcmd =~ /(.*apache2 -k start)/) {
1306
            $i++;
1307
        }
1308
    }
1309
    if ($i>1) {
1310
        print "Already backing up Stabile! ($i)\n";
1311
        return;
1312
    }
1313
    print "Backing up all images...\n";
1314

    
1315
    unless (tie %imagereg,'Tie::DBI', {
1316
        db=>'mysql:steamregister',
1317
        table=>'images',
1318
        key=>'path',
1319
        autocommit=>0,
1320
        CLOBBER=>3,
1321
        user=>$dbiuser,
1322
        password=>$dbipasswd}) {syslogit('info', "Image register could not be accessed")};
1323

    
1324
    my @regvalues = values %imagereg;
1325
    foreach my $valref (@regvalues) {
1326
        my $uuid = $valref->{'uuid'};
1327
        my $buser = $valref->{'user'};
1328
        my $bschedule = $valref->{'bschedule'};
1329
        if ($bschedule =~ /daily/) {
1330
            my $bcmd = qq|REMOTE_USER=$buser $base/cgi/images.cgi -a backup -u $uuid -g '{"skipzfs":1}'|;
1331
            print "Backing up ($bschedule): $valref->{'name'} ($uuid)\n";
1332
        # Actually do the backup
1333
            print `$bcmd`;
1334
        # If more than 4 jobs running, wait for some to finish
1335
            while (runningBackups()>4) {
1336
                print ".";
1337
                sleep 30;
1338
            }
1339
        } else {
1340
            print "Not backing up (" . ($bschedule?bschedule:'no schedule') . "): $valref->{'name'} ($uuid)\n";
1341
        # Update btime - this is done by zbackup below
1342
        #    my $bcmd = "REMOTE_USER=$buser $base/cgi/images.cgi -a updatebtime -u $uuid";
1343
        #    print `$bcmd`;
1344
        }
1345
    }
1346
    untie %imagereg;
1347
    # Finally back up all ZFS volumes
1348
    my $bcmd = "REMOTE_USER=irigo $base/cgi/images.cgi -a zbackup";
1349
    print `$bcmd`;
1350

    
1351
    sub runningBackups {
1352
        my $j = 0;
1353
        foreach my $valref (@regvalues) {
1354
            $j++ if ($valref->{'status'} =~ /backingup/);
1355
        }
1356
        return $j;
1357
    }
1358
}
1359

    
1360
sub syslogit {
1361
	my ($priority, $msg) = @_;
1362

    
1363
    my $current_time = time;
1364
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($current_time);
1365
    $year += 1900;
1366
    my $month = substr("0" . ($mon+1), -2);
1367
    my $pretty_time = sprintf "%4d-%02d-%02d@%02d:%02d:%02d",$year,$mon+1,$mday,$hour,$min,$sec;
1368

    
1369
	if ($msg && $msg ne '') {
1370
		unless (open(TEMP3, ">>$logfile")) {print "SError log file \"$logfile\" could not be written\n";}
1371
		print TEMP3 $pretty_time, " : $arg1 : $msg\n";
1372
		close(TEMP3);
1373
	}
1374
	return 0 unless ($priority =~ /err|debug/);
1375
	setlogsock('unix');
1376
	# $programname is assumed to be a global.  Also log the PID
1377
	# and to CONSole if there's a problem.  Use facility 'user'.
1378
	openlog($programname, 'pid,cons', 'user');
1379
	syslog($priority, $msg);
1380
	closelog();
1381
	return 1;
1382
}
1383

    
1384
sub getVirtualSize {
1385
    my $vpath = shift;
1386
    my $macip = shift;
1387
    my $qinfo;
1388
    my($bname, $dirpath, $suffix) = fileparse($vpath, (".vmdk", ".img", ".vhd", ".qcow", ".qcow2", ".vdi", ".iso"));
1389
    if ($suffix eq ".qcow2") {
1390
        if ($macip) {
1391
            $qinfo = `$sshcmd $macip /usr/bin/qemu-img info --force-share "$vpath"`;
1392
        } else {
1393
            $qinfo = `/usr/bin/qemu-img info --force-share "$vpath"`;
1394
        }
1395
        $qinfo =~ /virtual size:.*\((.+) bytes\)/g;
1396
        return(int($1)); # report size of new image for billing purposes
1397
    } elsif ($status eq ".vdi") {
1398
        if ($macip) {
1399
            $qinfo = `$sshcmd $macip /usr/bin/VBoxManage showhdinfo "$vpath"`;
1400
        } else {
1401
            $qinfo = `/usr/bin/VBoxManage showhdinfo "$vpath"`;
1402
        }
1403
        $qinfo =~ /Logical size:\s*(\d+) MBytes/g;
1404
        return(int($1) * 1024 * 1024); # report size of new image for billing purposes
1405
    } else {
1406
        if ($macip) {
1407
            return `$sshcmd $macip perl -e 'my @stat=stat("$vpath"); print $stat[7];'`;
1408
        } else {
1409
            my @stat = stat($vpath);
1410
            return($stat[7]); # report size of new image for billing purposes
1411
        }
1412
    }
1413
}
1414

    
1415
sub updateBilling {
1416
    my $imageuser = shift;
1417
    if ($imageuser && $imageuser ne 'common' && $imageuser ne 'guest') {
1418
        my $bcmd = "/usr/bin/$sshcmd 127.0.0.1 REMOTE_USER=$imageuser $base/cgi/images.cgi -a updatebilling";
1419
        my $ures = `$bcmd`;
1420
        debuglog("updating billing for $imageuser: $ures");
1421
    }
1422
}
1423

    
1424
sub debuglog {
1425
    my $msg = shift;
1426
    chomp $msg;
1427

    
1428
    open(my $fd, ">>", $debugfile);
1429
    print $fd "[$pretty_time] $msg\n";
1430
    close($fd);
1431
}
1432

    
1433
sub shell_esc_chars {
1434
	my $str = shift;
1435
	$str =~ s/([;<>\*\|`&\$!#\(\)\[\]\{\}:'" ])/\\$1/g;
1436
	return $str;
1437
}
(27-27/27)