4 # Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
6 # (Except where explictly superceded by other copyright notices)
8 # This work is made available to you under the terms of Version 2 of
9 # the GNU General Public License. A copy of that license should have
10 # been provided with this software, but in any event can be snarfed
13 # This work is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # General Public License for more details.
18 # Unless otherwise specified, all modifications, corrections or
19 # extensions to this work which alter its source code become the
20 # property of Best Practical Solutions, LLC when submitted for
21 # inclusion in the work.
27 use vars qw($PROMPT $VERSION $Handle $Nobody $SystemUser $item);
29 qw(@Groups @Users @ACL @Queues @ScripActions @ScripConditions @Templates @CustomFields @Scrips);
31 use lib "/opt/rt3/lib";
33 #This drags in RT's config.pm
34 # We do it in a begin block because RT::Handle needs to know the type to do its
46 use RT::ScripCondition;
58 'prompt-for-dba-password', 'force', 'debug',
59 'action=s', 'dba=s', 'dba-password=s', 'datafile=s',
63 $| = 1; #unbuffer that output.
66 my $Handle = RT::Handle->new($RT::DatabaseType);
70 if ( $args{'prompt-for-dba-password'} ) {
71 $args{'dba-password'} = get_dba_password();
72 chomp( $args{'dba-password'} );
75 unless ( $args{'action'} ) {
79 if ( $args{'action'} eq 'init' ) {
80 $dbh = DBI->connect( get_system_dsn(), $args{'dba'}, $args{'dba-password'} )
81 || die "Failed to connect to " . get_system_dsn() . " as $args{'dba'}: $DBI::errstr";
82 print "Now creating a database for RT.\n";
83 if ($RT::DatabaseType ne 'Oracle' ||
84 $args{'dba'} ne $RT::DatabaseUser) {
87 print "...skipped as ".$args{'dba'} ." is not " . $RT::DatabaseUser . " or we're working with Oracle.\n";
91 $dbh = DBI->connect( $Handle->DSN, $args{'dba'}, $args{'dba-password'} )
94 print "Now populating database schema.\n";
96 print "Now inserting database ACLs\n";
97 insert_acl() unless ($RT::DatabaseType eq 'Oracle');
98 print "Now inserting RT core system objects\n";
99 insert_initial_data();
100 print "Now inserting RT data\n";
101 insert_data( $RT::EtcPath . "/initialdata" );
103 elsif ( $args{'action'} eq 'drop' ) {
105 DBI->connect( get_system_dsn(), $args{'dba'}, $args{'dba-password'} ) )
108 warn "Database doesn't appear to exist. Aborting database drop.";
113 elsif ( $args{'action'} eq 'insert' ) {
114 insert_data( $args{'datafile'} );
116 elsif ($args{'action'} eq 'acl') {
117 $dbh = DBI->connect( $Handle->DSN, $args{'dba'}, $args{'dba-password'} )
118 || die "Failed to connect to " . get_system_dsn() . " as $args{'dba'}: $DBI::errstr";
119 insert_acl($args{'datadir'});
121 elsif ($args{'action'} eq 'schema') {
122 $dbh = DBI->connect( $Handle->DSN, $args{'dba'}, $args{'dba-password'} )
123 || die "Failed to connect to " . get_system_dsn() . " as $args{'dba'}: $DBI::errstr";
124 insert_schema($args{'datadir'});
128 print STDERR '$0 called with an invalid --action parameter';
132 # {{{ sub insert_schema
134 my $base_path = (shift || $RT::EtcPath);
136 print "Creating database schema.\n";
138 if ( -f $base_path . "/schema." . $RT::DatabaseType ) {
139 no warnings 'unopened';
141 open( SCHEMA, "<" . $base_path . "/schema." . $RT::DatabaseType );
142 open( SCHEMA_LOCAL, "<" . $RT::LocalEtcPath . "/schema." . $RT::DatabaseType );
145 foreach my $line (<SCHEMA>, ($_ = ';;'), <SCHEMA_LOCAL>) {
149 if ( $line =~ /;(\s*)$/ ) {
150 $statement =~ s/;(\s*)$//g;
151 push @schema, $statement;
156 local $SIG{__WARN__} = sub {};
157 my $is_local = 0; # local/etc/schema needs to be nonfatal.
158 foreach my $statement (@schema) {
159 if ($statement =~ /^\s*;$/) { $is_local = 1; next; }
160 print STDERR "SQL: $statement\n" if defined $args{'debug'};
161 my $sth = $dbh->prepare($statement) or die $dbh->errstr;
162 unless ( $sth->execute or $is_local ) {
163 die "Problem with statement:\n $statement\n" . $sth->errstr;
169 die "Couldn't find schema file for " . $RT::DatabaseType . "\n";
171 print "schema sucessfully inserted\n";
179 return if ( $RT::DatabaseType eq 'SQLite' );
180 if ( $RT::DatabaseType eq 'Oracle' ) {
183 To delete the tables and sequences of the RT Oracle database by running
190 unless ( $args{'force'} ) {
193 About to drop $RT::DatabaseType database $RT::DatabaseName on $RT::DatabaseHost.
194 WARNING: This will erase all data in $RT::DatabaseName.
197 exit unless _yesno();
201 print "Dropping $RT::DatabaseType database $RT::DatabaseName.\n";
203 $dbh->do("Drop DATABASE $RT::DatabaseName") or warn $DBI::errstr;
210 print "Creating $RT::DatabaseType database $RT::DatabaseName.\n";
211 if ( $RT::DatabaseType eq 'SQLite' ) {
214 elsif ( $RT::DatabaseType eq 'Pg' ) {
215 $dbh->do("CREATE DATABASE $RT::DatabaseName WITH ENCODING='UNICODE'");
217 $dbh->do("CREATE DATABASE $RT::DatabaseName") || die $DBI::errstr;
220 elsif ($RT::DatabaseType eq 'Oracle') {
223 elsif ( $RT::DatabaseType eq 'Informix' ) {
224 $ENV{DB_LOCALE} = 'en_us.utf8';
225 $dbh->do("CREATE DATABASE $RT::DatabaseName WITH BUFFERED LOG");
228 $dbh->do("CREATE DATABASE $RT::DatabaseName") or die $DBI::errstr;
234 sub get_dba_password {
236 "In order to create a new database and grant RT access to that database,\n";
237 print "this script needs to connect to your "
240 . $RT::DatabaseHost . " as "
241 . $args{'dba'} . ".\n";
243 "Please specify that user's database password below. If the user has no database\n";
244 print "password, just press return.\n\n";
247 my $password = ReadLine(0);
254 print "Proceed [y/N]:";
255 my $x = scalar(<STDIN>);
264 my $base_path = (shift || $RT::EtcPath);
266 if ( $RT::DatabaseType =~ /^oracle$/i ) {
267 do $base_path . "/acl.Oracle"
268 || die "Couldn't find ACLS for Oracle\n" . $@;
270 elsif ( $RT::DatabaseType =~ /^pg$/i ) {
271 do $base_path . "/acl.Pg" || die "Couldn't find ACLS for Pg\n" . $@;
273 elsif ( $RT::DatabaseType =~ /^mysql$/i ) {
274 do $base_path . "/acl.mysql"
275 || die "Couldn't find ACLS for mysql in " . $RT::EtcPath . "\n" . $@;
277 elsif ( $RT::DatabaseType =~ /^informix$/i ) {
278 do $base_path . "/acl.Informix"
279 || die "Couldn't find ACLS for Informix in " . $RT::EtcPath . "\n" . $@;
281 elsif ( $RT::DatabaseType =~ /^SQLite$/i ) {
285 die "Unknown RT database type";
289 foreach my $statement (@acl) {
290 print STDERR $statement if $args{'debug'};
291 my $sth = $dbh->prepare($statement) or die $dbh->errstr;
292 unless ( $sth->execute ) {
293 die "Problem with statement:\n $statement\n" . $sth->errstr;
300 =head2 get_system_dsn
302 Returns a dsn suitable for database creates and drops
303 and user creates and drops
309 my $dsn = $Handle->DSN;
311 #with mysql, you want to connect sans database to funge things
312 if ( $RT::DatabaseType eq 'mysql' ) {
313 $dsn =~ s/dbname=$RT::DatabaseName//;
315 # with postgres, you want to connect to database1
317 elsif ( $RT::DatabaseType eq 'Pg' ) {
318 $dsn =~ s/dbname=$RT::DatabaseName/dbname=template1/;
320 elsif ( $RT::DatabaseType eq 'Informix' ) {
321 # with Informix, you want to connect sans database:
322 $dsn =~ s/Informix:$RT::DatabaseName/Informix:/;
327 sub insert_initial_data {
331 #connect to the db, for actual RT work
333 $RT::Handle = RT::Handle->new();
334 $RT::Handle->Connect();
336 #Put together a current user object so we can create a User object
337 my $CurrentUser = new RT::CurrentUser();
339 print "Checking for existing system user ($CurrentUser)...";
340 my $test_user = RT::User->new($CurrentUser);
341 $test_user->Load('RT_System');
342 if ( $test_user->id ) {
343 print "found!\n\nYou appear to have a functional RT database.\n"
344 . "Exiting, so as not to clobber your existing data.\n";
349 print "not found. This appears to be a new installation.\n";
352 print "Creating system user...";
353 my $RT_System = new RT::User($CurrentUser);
355 my ( $val, $msg ) = $RT_System->_BootstrapCreate(
357 RealName => 'The RT System itself',
359 'Do not delete or modify this user. It is integral to RT\'s internal database structures',
367 $RT::Handle->Disconnect();
371 # load some sort of data into the database
374 my $datafile = shift;
376 #Connect to the database and get RT::SystemUser and RT::Nobody loaded
379 my $CurrentUser = RT::CurrentUser->new();
380 $CurrentUser->LoadByName('RT_System');
382 if ( $datafile eq $RT::EtcPath . "/initialdata" ) {
384 print "Creating Superuser ACL...";
386 my $superuser_ace = RT::ACE->new($CurrentUser);
387 $superuser_ace->_BootstrapCreate(
388 PrincipalId => ACLEquivGroupId( $CurrentUser->Id ),
389 PrincipalType => 'Group',
390 RightName => 'SuperUser',
391 ObjectType => 'RT::System',
396 # Slurp in stuff to insert from the datafile. Possible things to go in here:-
397 # @groups, @users, @acl, @queues, @ScripActions, @ScripConditions, @templates
400 || die "Couldn't find initial data for import\n" . $@;
403 print "Creating groups...";
404 foreach $item (@Groups) {
405 my $new_entry = RT::Group->new($CurrentUser);
406 my ( $return, $msg ) = $new_entry->_Create(%$item);
407 print "(Error: $msg)" unless ($return);
413 print "Creating users...";
414 foreach $item (@Users) {
415 my $new_entry = new RT::User($CurrentUser);
416 my ( $return, $msg ) = $new_entry->Create(%$item);
417 print "(Error: $msg)" unless ($return);
423 print "Creating queues...";
424 for $item (@Queues) {
425 my $new_entry = new RT::Queue($CurrentUser);
426 my ( $return, $msg ) = $new_entry->Create(%$item);
427 print "(Error: $msg)" unless ($return);
433 print "Creating ACL...";
434 for my $item (@ACL) {
436 my ($princ, $object);
438 # Global rights or Queue rights?
439 if ($item->{'Queue'}) {
440 $object = RT::Queue->new($CurrentUser);
441 $object->Load( $item->{'Queue'} );
443 $object = $RT::System;
446 # Group rights or user rights?
447 if ($item->{'GroupDomain'}) {
448 $princ = RT::Group->new($CurrentUser);
449 if ($item->{'GroupDomain'} eq 'UserDefined') {
450 $princ->LoadUserDefinedGroup( $item->{'GroupId'} );
451 } elsif ($item->{'GroupDomain'} eq 'SystemInternal') {
452 $princ->LoadSystemInternalGroup( $item->{'GroupType'} );
453 } elsif ($item->{'GroupDomain'} eq 'RT::Queue-Role' &&
455 $princ->LoadQueueRoleGroup( Type => $item->{'GroupType'},
456 Queue => $object->id);
458 $princ->Load( $item->{'GroupId'} );
461 $princ = RT::User->new($CurrentUser);
462 $princ->Load( $item->{'UserId'} );
466 my ( $return, $msg ) = $princ->PrincipalObj->GrantRight(
467 Right => $item->{'Right'},
482 print "Creating custom fields...";
483 for $item (@CustomFields) {
484 my $new_entry = new RT::CustomField($CurrentUser);
485 my $values = $item->{'Values'};
486 delete $item->{'Values'};
487 my $q = $item->{'Queue'};
488 my $q_obj = RT::Queue->new($CurrentUser);
491 $item->{'Queue'} = $q_obj->Id;
494 $item->{'Queue'} = 0;
497 print "(Error: Could not find queue " . $q . ")\n"
498 unless ( $q_obj->Id );
501 my ( $return, $msg ) = $new_entry->Create(%$item);
503 foreach my $value ( @{$values} ) {
504 my ( $eval, $emsg ) = $new_entry->AddValue(%$value);
505 print "(Error: $emsg)\n" unless ($eval);
508 print "(Error: $msg)\n" unless ($return);
516 print "Creating ScripActions...";
518 for $item (@ScripActions) {
519 my $new_entry = RT::ScripAction->new($CurrentUser);
520 my $return = $new_entry->Create(%$item);
527 if (@ScripConditions) {
528 print "Creating ScripConditions...";
530 for $item (@ScripConditions) {
531 my $new_entry = RT::ScripCondition->new($CurrentUser);
532 my $return = $new_entry->Create(%$item);
540 print "Creating templates...";
542 for $item (@Templates) {
543 my $new_entry = new RT::Template($CurrentUser);
544 my $return = $new_entry->Create(%$item);
550 print "Creating scrips...";
552 for $item (@Scrips) {
553 my $new_entry = new RT::Scrip($CurrentUser);
554 my ( $return, $msg ) = $new_entry->Create(%$item);
559 print "(Error: $msg)\n";
564 $RT::Handle->Disconnect();
568 =head2 ACLEquivGroupId
570 Given a userid, return that user's acl equivalence group
574 sub ACLEquivGroupId {
575 my $username = shift;
576 my $user = RT::User->new($RT::SystemUser);
577 $user->Load($username);
578 my $equiv_group = RT::Group->new($RT::SystemUser);
579 $equiv_group->LoadACLEquivalenceGroup($user);
580 return ( $equiv_group->Id );
587 $0: Set up RT's database
589 --action init Initialize the database
590 drop Drop the database.
591 This will ERASE ALL YOUR DATA
592 insert Insert data into RT's database.
593 By default, will use RT's installation data.
594 To use a local or supplementary datafile, specify it
595 using the '--datafile' option below.
597 acl Initialize only the database ACLs
598 To use a local or supplementary datafile, specify it
599 using the '--datadir' option below.
601 schema Initialize only the database schema
602 To use a local or supplementary datafile, specify it
603 using the '--datadir' option below.
605 --datafile /path/to/datafile
606 --datadir /path/to/ Used to specify a path to find the local
607 database schema and acls to be installed.
611 --dba-password dba's password
612 --prompt-for-dba-password Ask for the database administrator's password interactively