Re: How to run bconsole commands in a bash for loop?

Phil Stracchino <[email protected]>
Newsgroups gmane.comp.bacula.user
Organization Fenian House Publishing
Message-ID <[email protected]>
On 9/16/25 12:39, Gary Dale wrote:
> Internet searches have been futile. The ones that actually mention
> bconsole suggest what I've been trying to do should work, but it doesn't.
> 
> Through my initial setup struggles, my storage volumes have become a
> disjointed mess. To make matters worse, there are close to a hundred
> volumes showing "Error" when I "List Volumes". I have only about 20 that
> show up as physical disk volumes. And automatic volume creation seems to
> be failing. All my backups for the last 5 days have failed.

...


> Being able to put bconsole commands into a bash script seems like
> something that will be generally useful but I can't find a way to make
> it work.
> 
> Any ideas?


Willing to try a Perl script instead?

Try this one.  It does exactly what you're trying to do, including 
detecting errored volumes.

You'll need DBD::mysql or DBD::mariadb if you con't have them already 
installed and you're using a mysql/mariadb back end.  If you use 
PostgreSQL you'll have to make some changes.  Try it with -t first as a 
dry-run to make sure it's going to do what you want it to.


-- 
   Phil Stracchino
   Fenian House Publishing
   [email protected]
   [email protected]
   Landline: +1.603.293.8485
   Mobile:   +1.603.998.6958

_______________________________________________
Bacula-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/bacula-users
clean_volumes (text/plain, 4.9 KB)
#!/usr/bin/perl
use strict;
use DBD::mysql;
use Getopt::Long;
use IPC::Open2;
use IO::Handle;


my $bconsole = '/usr/sbin/bconsole';
my (%opts, @purged, @orphans, $pid, $volume_dir);

Getopt::Long::Configure('bundling');
GetOptions(\%opts,
           'verbose|v',
           'test|t');

$volume_dir = shift() || '/bpool/bacula';

print "Volume directory is $volume_dir\n" if ($opts{verbose});;

my ($IN, $OUT) = (IO::Handle->new(), IO::Handle->new());

$pid = open2($OUT, $IN, $bconsole) || die "Unable to open bconsole";

if (check_volumes())
{
    my $deleted;
    
    if (scalar @purged)
    {
        printf("Bacula reports the following purged or errored volumes:\n\t%s\n",
               join("\n\t", @purged)) if ($opts{verbose});
        $deleted = delete_volumes(@purged) unless ($opts{test});
        print "$deleted purged volumes deleted.\n" if ($opts{verbose});
    }
    elsif ($opts{verbose})
    {
        print "No purged volumes found to delete.\n";
    }

    if (scalar @orphans)
    {
        printf("The following orphaned volumes were found:\n\t%s\n",
               join("\n\t", @orphans)) if ($opts{verbose});
        $deleted = delete_orphans(@orphans) unless ($opts{test});
        print "$deleted orphan volume files deleted.\n" if ($opts{verbose});
    }
    elsif ($opts{verbose})
    {
        print "No orphaned volumes found to delete.\n";
    }
}
elsif ($opts{verbose})
{
    print "Nothing found to do.\n";
}

print $IN "exit\n";
waitpid($pid, 0);

exit (0);


sub check_volumes
{
    my $dividers = 0;
    my (@row, @files);

    print $IN "list volumes pool=Scratch\n";
    for (;;)
    {
        my $resp = <$OUT>;
        last if ($resp =~ /No results to list./);
        $dividers++ if ($resp =~ /^[\+\-]+$/);
        last if ($dividers == 3);
        @row = split(/\s+/, $resp);
        push (@purged, $row[3]) if ($row[5] eq 'Purged');
    }

    print $IN "list volumes\n";
    for (;;)
    {
        my $resp = <$OUT>;
        last if ($resp =~ /No results to list.|Pool: Scratch/ );
        $dividers++ if ($resp =~ /^[\+\-]+$/);
        @row = split(/\s+/, $resp);
        push (@purged, $row[3]) if ($row[5] eq 'Error');
    }

    if ( -d $volume_dir )
    {
        my ($dbh, $sth, %volumes);
        
        print "Scanning for orphans in $volume_dir ...\n" if ($opts{verbose});

        opendir (DIR, $volume_dir);
        @files = sort(grep(!/^\.{1,2}$/, readdir (DIR)));
        closedir (DIR);
    
        printf ("Files found on disk:\n\t%s\n",
                join("\n\t", @files)) if ($opts{verbose});
    
        $dbh = open_db();
        $sth = $dbh->prepare('SELECT volumeName FROM bacula.Media') || die "Error:" . $dbh->errstr . "\n";
        $sth->execute || die "Error:" . $dbh->errstr . "\n";
        while (my $ref = $sth->fetchrow_arrayref)
        {
            $volumes{$$ref[0]} = 1;
        }
        $sth->finish();
        $dbh->disconnect();

        printf ("Volumes found in media table:\n\t%s\n",
                join("\n\t", sort keys %volumes)) if ($opts{verbose});

        foreach my $f (@files)
        {
            next unless ($f =~ /^(FULL|DIFF|INCR)/);
            print "Checking file $f against volume list\n" if ($opts{verbose});
        
            push (@orphans, $f) if (not defined $volumes{$f});
        }
    }

    return (scalar @purged + scalar @orphans);
}


sub delete_volumes
{
    my $count = 0;

    foreach my $vol (@_)
    {
        my $l;
        my $file = $volume_dir.'/'.$vol;

        print "Deleting volume $vol from catalog ... " if ($opts{verbose});
        print $IN "delete volume=$vol yes\n";
        $l = <$OUT>;
        $l = <$OUT>;
        print "Done.\nDeleting volume $file from disk ... " if ($opts{verbose});
        if (-f $file)
        {
            $count++;
            unlink ($file);
        }
        print "Done.\n" if ($opts{verbose});
    }

    return ($count);
}


sub delete_orphans
{
    my $count = 0;

    foreach my $vol (@_)
    {
        my $l;
        my $file = $volume_dir.'/'.$vol;

        print "Deleting volume $file from disk ... " if ($opts{verbose});
        if (-f $file)
        {
            $count++;
            unlink ($file);
        }
        print "Done.\n" if ($opts{verbose});
    }

    return ($count);
}


sub open_db
{
    my ($dsn, $dbh);

    $dsn = $opts{socket} && -e $opts{socket}
         ? sprintf("DBI:mysql:mysql:host=localhost;mysql_read_default_file=%s;mysql_read_default_group=mysql;mysql_socket=%s;",
                   '/root/.my.cnf',
                   $opts{socket})
         : sprintf("DBI:mysql:mysql:host=localhost;mysql_read_default_file=%s;mysql_read_default_group=mysql;",
                   '/root/.my.cnf');

    $dbh = DBI->connect($dsn, undef, undef, {RaiseError => 1, AutoCommit => 0});
    $dbh->do(sprintf('SET SESSION wait_timeout = %d',
                     $opts{interval} > 3500 ? $opts{interval} + 100 : 3600)) || die "Could not set session wait_timeout";

    return ($dbh);
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.