]>
Commit | Line | Data |
---|---|---|
1 | #!/usr/bin/perl | |
2 | # BEGIN BPS TAGGED BLOCK {{{ | |
3 | # | |
4 | # COPYRIGHT: | |
5 | # | |
6 | # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC | |
7 | # <sales@bestpractical.com> | |
8 | # | |
9 | # (Except where explicitly superseded by other copyright notices) | |
10 | # | |
11 | # | |
12 | # LICENSE: | |
13 | # | |
14 | # This work is made available to you under the terms of Version 2 of | |
15 | # the GNU General Public License. A copy of that license should have | |
16 | # been provided with this software, but in any event can be snarfed | |
17 | # from www.gnu.org. | |
18 | # | |
19 | # This work is distributed in the hope that it will be useful, but | |
20 | # WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
22 | # General Public License for more details. | |
23 | # | |
24 | # You should have received a copy of the GNU General Public License | |
25 | # along with this program; if not, write to the Free Software | |
26 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
27 | # 02110-1301 or visit their web page on the internet at | |
28 | # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. | |
29 | # | |
30 | # | |
31 | # CONTRIBUTION SUBMISSION POLICY: | |
32 | # | |
33 | # (The following paragraph is not intended to limit the rights granted | |
34 | # to you to modify and distribute this software under the terms of | |
35 | # the GNU General Public License and is only of importance to you if | |
36 | # you choose to contribute your changes and enhancements to the | |
37 | # community by submitting them to Best Practical Solutions, LLC.) | |
38 | # | |
39 | # By intentionally submitting any modifications, corrections or | |
40 | # derivatives to this work, or any other work intended for use with | |
41 | # Request Tracker, to Best Practical Solutions, LLC, you confirm that | |
42 | # you are the copyright holder for those contributions and you grant | |
43 | # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, | |
44 | # royalty-free, perpetual, license to use, copy, create derivative | |
45 | # works based on those contributions, and sublicense and distribute | |
46 | # those contributions and any derivatives thereof. | |
47 | # | |
48 | # END BPS TAGGED BLOCK }}} | |
49 | use strict; | |
50 | use warnings; | |
51 | ||
52 | use vars qw($Nobody $SystemUser $item); | |
53 | ||
54 | # fix lib paths, some may be relative | |
55 | BEGIN { | |
56 | require File::Spec; | |
57 | my @libs = ("lib", "local/lib"); | |
58 | my $bin_path; | |
59 | ||
60 | for my $lib (@libs) { | |
61 | unless ( File::Spec->file_name_is_absolute($lib) ) { | |
62 | unless ($bin_path) { | |
63 | if ( File::Spec->file_name_is_absolute(__FILE__) ) { | |
64 | $bin_path = ( File::Spec->splitpath(__FILE__) )[1]; | |
65 | } | |
66 | else { | |
67 | require FindBin; | |
68 | no warnings "once"; | |
69 | $bin_path = $FindBin::Bin; | |
70 | } | |
71 | } | |
72 | $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib ); | |
73 | } | |
74 | unshift @INC, $lib; | |
75 | } | |
76 | ||
77 | } | |
78 | ||
79 | use Term::ReadKey; | |
80 | use Getopt::Long; | |
81 | ||
82 | $| = 1; # unbuffer all output. | |
83 | ||
84 | my %args = ( | |
85 | dba => 'postgres', | |
86 | package => 'RT', | |
87 | ); | |
88 | GetOptions( | |
89 | \%args, | |
90 | 'action=s', | |
91 | 'force', 'debug', | |
92 | 'dba=s', 'dba-password=s', 'prompt-for-dba-password', 'package=s', | |
93 | 'datafile=s', 'datadir=s', 'skip-create', 'root-password-file=s', | |
94 | 'help|h', | |
95 | ); | |
96 | ||
97 | no warnings 'once'; | |
98 | if ( $args{help} || ! $args{'action'} ) { | |
99 | require Pod::Usage; | |
100 | Pod::Usage::pod2usage({ verbose => 2 }); | |
101 | exit; | |
102 | } | |
103 | ||
104 | require RT; | |
105 | RT->LoadConfig(); | |
106 | RT->InitClasses(); | |
107 | ||
108 | # Force warnings to be output to STDERR if we're not already logging | |
109 | # them at a higher level | |
110 | RT->Config->Set( LogToScreen => 'warning') | |
111 | unless ( RT->Config->Get( 'LogToScreen' ) | |
112 | && RT->Config->Get( 'LogToScreen' ) =~ /^(debug|info|notice)$/ ); | |
113 | ||
114 | # get customized root password | |
115 | my $root_password; | |
116 | if ( $args{'root-password-file'} ) { | |
117 | open( my $fh, '<', $args{'root-password-file'} ) | |
118 | or die "Couldn't open 'args{'root-password-file'}' for reading: $!"; | |
119 | $root_password = <$fh>; | |
120 | chomp $root_password; | |
121 | my $min_length = RT->Config->Get('MinimumPasswordLength'); | |
122 | if ($min_length) { | |
123 | die | |
124 | "password needs to be at least $min_length long, please check file '$args{'root-password-file'}'" | |
125 | if length $root_password < $min_length; | |
126 | } | |
127 | close $fh; | |
128 | } | |
129 | ||
130 | ||
131 | # check and setup @actions | |
132 | my @actions = grep $_, split /,/, $args{'action'}; | |
133 | if ( @actions > 1 && $args{'datafile'} ) { | |
134 | print STDERR "You can not use --datafile option with multiple actions.\n"; | |
135 | exit(-1); | |
136 | } | |
137 | foreach ( @actions ) { | |
138 | unless ( /^(?:init|create|drop|schema|acl|coredata|insert|upgrade)$/ ) { | |
139 | print STDERR "$0 called with an invalid --action parameter.\n"; | |
140 | exit(-1); | |
141 | } | |
142 | if ( /^(?:init|drop|upgrade)$/ && @actions > 1 ) { | |
143 | print STDERR "You can not mix init, drop or upgrade action with any action.\n"; | |
144 | exit(-1); | |
145 | } | |
146 | } | |
147 | ||
148 | # convert init to multiple actions | |
149 | my $init = 0; | |
150 | if ( $actions[0] eq 'init' ) { | |
151 | if ($args{'skip-create'}) { | |
152 | @actions = qw(schema coredata insert); | |
153 | } else { | |
154 | @actions = qw(create schema acl coredata insert); | |
155 | } | |
156 | $init = 1; | |
157 | } | |
158 | ||
159 | # set options from environment | |
160 | foreach my $key(qw(Type Host Name User Password)) { | |
161 | next unless exists $ENV{ 'RT_DB_'. uc $key }; | |
162 | print "Using Database$key from RT_DB_". uc($key) ." environment variable.\n"; | |
163 | RT->Config->Set( "Database$key", $ENV{ 'RT_DB_'. uc $key }); | |
164 | } | |
165 | ||
166 | my $db_type = RT->Config->Get('DatabaseType') || ''; | |
167 | my $db_host = RT->Config->Get('DatabaseHost') || ''; | |
168 | my $db_port = RT->Config->Get('DatabasePort') || ''; | |
169 | my $db_name = RT->Config->Get('DatabaseName') || ''; | |
170 | my $db_user = RT->Config->Get('DatabaseUser') || ''; | |
171 | my $db_pass = RT->Config->Get('DatabasePassword') || ''; | |
172 | ||
173 | # load it here to get error immidiatly if DB type is not supported | |
174 | require RT::Handle; | |
175 | ||
176 | if ( $db_type eq 'SQLite' && !File::Spec->file_name_is_absolute($db_name) ) { | |
177 | $db_name = File::Spec->catfile($RT::VarPath, $db_name); | |
178 | RT->Config->Set( DatabaseName => $db_name ); | |
179 | } | |
180 | ||
181 | my $dba_user = $args{'dba'} || $ENV{'RT_DBA_USER'} || $db_user || ''; | |
182 | my $dba_pass = $args{'dba-password'} || $ENV{'RT_DBA_PASSWORD'}; | |
183 | ||
184 | if ($args{'skip-create'}) { | |
185 | $dba_user = $db_user; | |
186 | $dba_pass = $db_pass; | |
187 | } else { | |
188 | if ( !$args{force} && ( !defined $dba_pass || $args{'prompt-for-dba-password'} ) ) { | |
189 | $dba_pass = get_dba_password(); | |
190 | chomp $dba_pass if defined($dba_pass); | |
191 | } | |
192 | } | |
193 | ||
194 | my $version_word_regex = join '|', RT::Handle->version_words; | |
195 | my $version_dir = qr/^\d+\.\d+\.\d+(?:$version_word_regex)?\d*$/; | |
196 | ||
197 | print "Working with:\n" | |
198 | ."Type:\t$db_type\nHost:\t$db_host\nPort:\t$db_port\nName:\t$db_name\n" | |
199 | ."User:\t$db_user\nDBA:\t$dba_user" . ($args{'skip-create'} ? ' (No DBA)' : '') . "\n"; | |
200 | ||
201 | foreach my $action ( @actions ) { | |
202 | no strict 'refs'; | |
203 | my ($status, $msg) = *{ 'action_'. $action }{'CODE'}->( %args ); | |
204 | error($action, $msg) unless $status; | |
205 | print $msg .".\n" if $msg; | |
206 | print "Done.\n"; | |
207 | } | |
208 | ||
209 | sub action_create { | |
210 | my %args = @_; | |
211 | my $dbh = get_system_dbh(); | |
212 | my ($status, $msg) = RT::Handle->CheckCompatibility( $dbh, 'create' ); | |
213 | return ($status, $msg) unless $status; | |
214 | ||
215 | print "Now creating a $db_type database $db_name for RT.\n"; | |
216 | return RT::Handle->CreateDatabase( $dbh ); | |
217 | } | |
218 | ||
219 | sub action_drop { | |
220 | my %args = @_; | |
221 | ||
222 | print "Dropping $db_type database $db_name.\n"; | |
223 | unless ( $args{'force'} ) { | |
224 | print <<END; | |
225 | ||
226 | About to drop $db_type database $db_name on $db_host (port '$db_port'). | |
227 | WARNING: This will erase all data in $db_name. | |
228 | ||
229 | END | |
230 | exit(-2) unless _yesno(); | |
231 | } | |
232 | ||
233 | my $dbh = get_system_dbh(); | |
234 | return RT::Handle->DropDatabase( $dbh ); | |
235 | } | |
236 | ||
237 | sub action_schema { | |
238 | my %args = @_; | |
239 | my $dbh = get_admin_dbh(); | |
240 | my ($status, $msg) = RT::Handle->CheckCompatibility( $dbh, 'schema' ); | |
241 | return ($status, $msg) unless $status; | |
242 | ||
243 | print "Now populating database schema.\n"; | |
244 | return RT::Handle->InsertSchema( $dbh, $args{'datafile'} || $args{'datadir'} ); | |
245 | } | |
246 | ||
247 | sub action_acl { | |
248 | my %args = @_; | |
249 | my $dbh = get_admin_dbh(); | |
250 | my ($status, $msg) = RT::Handle->CheckCompatibility( $dbh, 'acl' ); | |
251 | return ($status, $msg) unless $status; | |
252 | ||
253 | print "Now inserting database ACLs.\n"; | |
254 | return RT::Handle->InsertACL( $dbh, $args{'datafile'} || $args{'datadir'} ); | |
255 | } | |
256 | ||
257 | sub action_coredata { | |
258 | my %args = @_; | |
259 | $RT::Handle = RT::Handle->new; | |
260 | $RT::Handle->dbh( undef ); | |
261 | RT::ConnectToDatabase(); | |
262 | RT::InitLogging(); | |
263 | my ($status, $msg) = RT::Handle->CheckCompatibility( $RT::Handle->dbh, 'coredata' ); | |
264 | return ($status, $msg) unless $status; | |
265 | ||
266 | print "Now inserting RT core system objects.\n"; | |
267 | return $RT::Handle->InsertInitialData; | |
268 | } | |
269 | ||
270 | sub action_insert { | |
271 | my %args = @_; | |
272 | $RT::Handle = RT::Handle->new; | |
273 | RT::Init(); | |
274 | my ($status, $msg) = RT::Handle->CheckCompatibility( $RT::Handle->dbh, 'insert' ); | |
275 | return ($status, $msg) unless $status; | |
276 | ||
277 | print "Now inserting data.\n"; | |
278 | my $file = $args{'datafile'}; | |
279 | $file = $RT::EtcPath . "/initialdata" if $init && !$file; | |
280 | $file ||= $args{'datadir'}."/content"; | |
281 | ||
282 | # Slurp in backcompat | |
283 | my %removed; | |
284 | my @back = @{$args{backcompat} || []}; | |
285 | if (@back) { | |
286 | my @lines = do {local @ARGV = @back; <>}; | |
287 | for (@lines) { | |
288 | s/\#.*//; | |
289 | next unless /\S/; | |
290 | my ($class, @fields) = split; | |
291 | $class->_BuildTableAttributes; | |
292 | $RT::Logger->debug("Temporarily removing @fields from $class"); | |
293 | $removed{$class}{$_} = delete $RT::Record::_TABLE_ATTR->{$class}{$_} | |
294 | for @fields; | |
295 | } | |
296 | } | |
297 | ||
298 | my @ret = $RT::Handle->InsertData( $file, $root_password ); | |
299 | ||
300 | # Put back the fields we chopped off | |
301 | for my $class (keys %removed) { | |
302 | $RT::Record::_TABLE_ATTR->{$class}{$_} = $removed{$class}{$_} | |
303 | for keys %{$removed{$class}}; | |
304 | } | |
305 | return @ret; | |
306 | } | |
307 | ||
308 | sub action_upgrade { | |
309 | my %args = @_; | |
310 | my $base_dir = $args{'datadir'} || "./etc/upgrade"; | |
311 | return (0, "Couldn't read dir '$base_dir' with upgrade data") | |
312 | unless -d $base_dir || -r _; | |
313 | ||
314 | my $upgrading_from = undef; | |
315 | do { | |
316 | if ( defined $upgrading_from ) { | |
317 | print "Doesn't match #.#.#: "; | |
318 | } else { | |
319 | print "Enter $args{package} version you're upgrading from: "; | |
320 | } | |
321 | $upgrading_from = scalar <STDIN>; | |
322 | chomp $upgrading_from; | |
323 | $upgrading_from =~ s/\s+//g; | |
324 | } while $upgrading_from !~ /$version_dir/; | |
325 | ||
326 | my $upgrading_to = $RT::VERSION; | |
327 | return (0, "The current version $upgrading_to is lower than $upgrading_from") | |
328 | if RT::Handle::cmp_version( $upgrading_from, $upgrading_to ) > 0; | |
329 | ||
330 | return (1, "The version $upgrading_to you're upgrading to is up to date") | |
331 | if RT::Handle::cmp_version( $upgrading_from, $upgrading_to ) == 0; | |
332 | ||
333 | my @versions = get_versions_from_to($base_dir, $upgrading_from, undef); | |
334 | return (1, "No DB changes since $upgrading_from") | |
335 | unless @versions; | |
336 | ||
337 | if (RT::Handle::cmp_version($versions[-1], $upgrading_to) > 0) { | |
338 | print "\n***** There are upgrades for $versions[-1], which is later than $upgrading_to,\n"; | |
339 | print "***** which you are nominally upgrading to. Upgrading to $versions[-1] instead.\n"; | |
340 | $upgrading_to = $versions[-1]; | |
341 | } | |
342 | ||
343 | print "\nGoing to apply following upgrades:\n"; | |
344 | print map "* $_\n", @versions; | |
345 | ||
346 | { | |
347 | my $custom_upgrading_to = undef; | |
348 | do { | |
349 | if ( defined $custom_upgrading_to ) { | |
350 | print "Doesn't match #.#.#: "; | |
351 | } else { | |
352 | print "\nEnter $args{package} version if you want to stop upgrade at some point,\n"; | |
353 | print " or leave it blank if you want apply above upgrades: "; | |
354 | } | |
355 | $custom_upgrading_to = scalar <STDIN>; | |
356 | chomp $custom_upgrading_to; | |
357 | $custom_upgrading_to =~ s/\s+//g; | |
358 | last unless $custom_upgrading_to; | |
359 | } while $custom_upgrading_to !~ /$version_dir/; | |
360 | ||
361 | if ( $custom_upgrading_to ) { | |
362 | return ( | |
363 | 0, "The version you entered ($custom_upgrading_to) is lower than\n" | |
364 | ."version you're upgrading from ($upgrading_from)" | |
365 | ) if RT::Handle::cmp_version( $upgrading_from, $custom_upgrading_to ) > 0; | |
366 | ||
367 | return (1, "The version you're upgrading to is up to date") | |
368 | if RT::Handle::cmp_version( $upgrading_from, $custom_upgrading_to ) == 0; | |
369 | ||
370 | if ( RT::Handle::cmp_version( $RT::VERSION, $custom_upgrading_to ) < 0 ) { | |
371 | print "Version you entered is greater than installed ($RT::VERSION).\n"; | |
372 | _yesno() or exit(-2); | |
373 | } | |
374 | # ok, checked everything no let's refresh list | |
375 | $upgrading_to = $custom_upgrading_to; | |
376 | @versions = get_versions_from_to($base_dir, $upgrading_from, $upgrading_to); | |
377 | ||
378 | return (1, "No DB changes between $upgrading_from and $upgrading_to") | |
379 | unless @versions; | |
380 | ||
381 | print "\nGoing to apply following upgrades:\n"; | |
382 | print map "* $_\n", @versions; | |
383 | } | |
384 | } | |
385 | ||
386 | print "\nIT'S VERY IMPORTANT TO BACK UP BEFORE THIS STEP\n\n"; | |
387 | _yesno() or exit(-2) unless $args{'force'}; | |
388 | ||
389 | my ( $ret, $msg ); | |
390 | foreach my $n ( 0..$#versions ) { | |
391 | my $v = $versions[$n]; | |
392 | my @back = grep {-e $_} map {"$base_dir/$versions[$_]/backcompat"} $n+1..$#versions; | |
393 | print "Processing $v\n"; | |
394 | my %tmp = (%args, datadir => "$base_dir/$v", datafile => undef, backcompat => \@back); | |
395 | if ( -e "$base_dir/$v/schema.$db_type" ) { | |
396 | ( $ret, $msg ) = action_schema( %tmp ); | |
397 | return ( $ret, $msg ) unless $ret; | |
398 | } | |
399 | if ( -e "$base_dir/$v/acl.$db_type" ) { | |
400 | ( $ret, $msg ) = action_acl( %tmp ); | |
401 | return ( $ret, $msg ) unless $ret; | |
402 | } | |
403 | if ( -e "$base_dir/$v/content" ) { | |
404 | ( $ret, $msg ) = action_insert( %tmp ); | |
405 | return ( $ret, $msg ) unless $ret; | |
406 | } | |
407 | } | |
408 | return 1; | |
409 | } | |
410 | ||
411 | sub get_versions_from_to { | |
412 | my ($base_dir, $from, $to) = @_; | |
413 | ||
414 | opendir( my $dh, $base_dir ) or die "couldn't open dir: $!"; | |
415 | my @versions = grep -d "$base_dir/$_" && /$version_dir/, readdir $dh; | |
416 | closedir $dh; | |
417 | ||
418 | die "\nERROR: No upgrade data found in '$base_dir'! Perhaps you specified the wrong --datadir?\n" | |
419 | unless @versions; | |
420 | ||
421 | return | |
422 | grep defined $to ? RT::Handle::cmp_version($_, $to) <= 0 : 1, | |
423 | grep RT::Handle::cmp_version($_, $from) > 0, | |
424 | sort RT::Handle::cmp_version @versions; | |
425 | } | |
426 | ||
427 | sub error { | |
428 | my ($action, $msg) = @_; | |
429 | print STDERR "Couldn't finish '$action' step.\n\n"; | |
430 | print STDERR "ERROR: $msg\n\n"; | |
431 | exit(-1); | |
432 | } | |
433 | ||
434 | sub get_dba_password { | |
435 | print "In order to create or update your RT database," | |
436 | . " this script needs to connect to your " | |
437 | . " $db_type instance on $db_host (port '$db_port') as $dba_user\n"; | |
438 | print "Please specify that user's database password below. If the user has no database\n"; | |
439 | print "password, just press return.\n\n"; | |
440 | print "Password: "; | |
441 | ReadMode('noecho'); | |
442 | my $password = ReadLine(0); | |
443 | ReadMode('normal'); | |
444 | print "\n"; | |
445 | return ($password); | |
446 | } | |
447 | ||
448 | # get_system_dbh | |
449 | # Returns L<DBI> database handle connected to B<system> with DBA credentials. | |
450 | # See also L<RT::Handle/SystemDSN>. | |
451 | ||
452 | ||
453 | sub get_system_dbh { | |
454 | return _get_dbh( RT::Handle->SystemDSN, $dba_user, $dba_pass ); | |
455 | } | |
456 | ||
457 | sub get_admin_dbh { | |
458 | return _get_dbh( RT::Handle->DSN, $dba_user, $dba_pass ); | |
459 | } | |
460 | ||
461 | # get_rt_dbh [USER, PASSWORD] | |
462 | ||
463 | # Returns L<DBI> database handle connected to RT database, | |
464 | # you may specify credentials(USER and PASSWORD) to connect | |
465 | # with. By default connects with credentials from RT config. | |
466 | ||
467 | sub get_rt_dbh { | |
468 | return _get_dbh( RT::Handle->DSN, $db_user, $db_pass ); | |
469 | } | |
470 | ||
471 | sub _get_dbh { | |
472 | my ($dsn, $user, $pass) = @_; | |
473 | my $dbh = DBI->connect( | |
474 | $dsn, $user, $pass, | |
475 | { RaiseError => 0, PrintError => 0 }, | |
476 | ); | |
477 | unless ( $dbh ) { | |
478 | my $msg = "Failed to connect to $dsn as user '$user': ". $DBI::errstr; | |
479 | if ( $args{'debug'} ) { | |
480 | require Carp; Carp::confess( $msg ); | |
481 | } else { | |
482 | print STDERR $msg; exit -1; | |
483 | } | |
484 | } | |
485 | return $dbh; | |
486 | } | |
487 | ||
488 | sub _yesno { | |
489 | print "Proceed [y/N]:"; | |
490 | my $x = scalar(<STDIN>); | |
491 | $x =~ /^y/i; | |
492 | } | |
493 | ||
494 | 1; | |
495 | ||
496 | __END__ | |
497 | ||
498 | =head1 NAME | |
499 | ||
500 | rt-setup-database - Set up RT's database | |
501 | ||
502 | =head1 SYNOPSIS | |
503 | ||
504 | rt-setup-database --action ... | |
505 | ||
506 | =head1 OPTIONS | |
507 | ||
508 | =over | |
509 | ||
510 | =item action | |
511 | ||
512 | Several actions can be combined using comma separated list. | |
513 | ||
514 | =over | |
515 | ||
516 | =item init | |
517 | ||
518 | Initialize the database. This is combination of multiple actions listed below. | |
519 | Create DB, schema, setup acl, insert core data and initial data. | |
520 | ||
521 | =item upgrade | |
522 | ||
523 | Apply all needed schema/acl/content updates (will ask for version to upgrade | |
524 | from) | |
525 | ||
526 | =item create | |
527 | ||
528 | Create the database. | |
529 | ||
530 | =item drop | |
531 | ||
532 | Drop the database. This will B<ERASE ALL YOUR DATA>. | |
533 | ||
534 | =item schema | |
535 | ||
536 | Initialize only the database schema | |
537 | ||
538 | To use a local or supplementary datafile, specify it using the '--datadir' | |
539 | option below. | |
540 | ||
541 | =item acl | |
542 | ||
543 | Initialize only the database ACLs | |
544 | ||
545 | To use a local or supplementary datafile, specify it using the '--datadir' | |
546 | option below. | |
547 | ||
548 | =item coredata | |
549 | ||
550 | Insert data into RT's database. This data is required for normal functioning of | |
551 | any RT instance. | |
552 | ||
553 | =item insert | |
554 | ||
555 | Insert data into RT's database. By default, will use RT's installation data. | |
556 | To use a local or supplementary datafile, specify it using the '--datafile' | |
557 | option below. | |
558 | ||
559 | =back | |
560 | ||
561 | =item datafile | |
562 | ||
563 | file path of the data you want to action on | |
564 | ||
565 | e.g. C<--datafile /path/to/datafile> | |
566 | ||
567 | =item datadir | |
568 | ||
569 | Used to specify a path to find the local database schema and acls to be | |
570 | installed. | |
571 | ||
572 | e.g. C<--datadir /path/to/> | |
573 | ||
574 | =item dba | |
575 | ||
576 | dba's username | |
577 | ||
578 | =item dba-password | |
579 | ||
580 | dba's password | |
581 | ||
582 | =item prompt-for-dba-password | |
583 | ||
584 | Ask for the database administrator's password interactively | |
585 | ||
586 | =item skip-create | |
587 | ||
588 | for 'init': skip creating the database and the user account, so we don't need | |
589 | administrator privileges | |
590 | ||
591 | =item root-password-file | |
592 | ||
593 | for 'init' and 'insert': rather than using the default administrative password | |
594 | for RT's "root" user, use the password in this file. | |
595 | ||
596 | =back |