1#!/usr/bin/perl
2# Copyright (c) 2013 Apple Inc. All Rights Reserved.
3#
4# IMPORTANT NOTE: This file is licensed only for use on Apple-branded
5# computers and is subject to the terms and conditions of the Apple Software
6# License Agreement accompanying the package this file is a part of.
7# You may not port this file to another platform without Apple's written consent.
8
9# Wrapper script for postgres; waits for dataPath to be mounted before proceeding
10# Reads additional arguments from a plist specified by optional
11#   "--apple-configuration" flag. These args are appended to the original ARGV, and
12#   the --apple-configuration args are removed, before handing the final set of args
13#   to the real postgres executable.
14
15use strict;
16use warnings;
17use Getopt::Std;
18
19my $PLIST_BUDDY = '/usr/libexec/PlistBuddy';
20my $WAIT4PATH   = '/bin/wait4path';
21
22my $postgres_real_path = '@PATH_TEMPLATE@';
23my $config_path;
24my $data_directory;
25my $g_child_status;
26my @postgres_argv = @ARGV;
27
28our $opt_D;
29getopt('D:');
30
31if ( defined $opt_D ) {
32    $data_directory = $opt_D;
33}
34
35for ( my $i = 0; $i < $#postgres_argv; $i++ ) {
36    if ( $postgres_argv[$i] eq '--apple-configuration' ) {
37        if ( !defined $postgres_argv[ $i + 1 ] ) {
38            print "Error: missing required argument for --apple-configuration\n";
39            exit 1;
40        }
41        $config_path = $postgres_argv[ $i + 1 ];
42        splice @postgres_argv, $i, 2;
43        last;
44    }
45}
46
47if ( -e $config_path ) {
48    my @config_lines = `$PLIST_BUDDY -c 'Print :ProgramArguments' "$config_path"`;
49
50    # Skipping the first and last lines, which specify the class type.
51    for ( my $i = 1; $i < $#config_lines; $i++ ) {
52        if ( $config_lines[$i] =~ / \A \s* (.+?) \s* \n* \z /xms ) {
53            push @postgres_argv, $1;
54        }
55        if ( !defined $data_directory
56            && $config_lines[$i] =~ / \A \s* -D \s* \n* \z /xms )
57        {
58            if ( defined $config_lines[ $i + 1 ]
59                && $config_lines[ $i + 1 ] =~ / \A \s* (.+?) \s* \n* \z /xms )
60            {
61                $data_directory = $1;
62            }
63        }
64    }
65}
66
67if ( defined $data_directory ) {
68
69    # Snip the shared memory block out of the lockfile if no process is running
70    # that matches the PID in the file.  Otherwise postgres will fail to start
71    # if it wasn't shut down properly and another process is now using that memory
72    # block.
73    my $postgres_pid_path = $data_directory . '/postmaster.pid';
74    if ( -e $postgres_pid_path ) {
75        my $FILE;
76        if ( !open $FILE, '+<', $postgres_pid_path ) {
77            print "Error opening lock file: $!\n";
78        }
79        else {
80            my @lines = <$FILE>;
81            if ( $lines[0] =~ / \A (\d+) \n* \z /xms ) {
82                my $old_pid = $1;
83                my $ret = system 'kill', '-0', $old_pid;
84                if ( $ret != 0 ) {
85
86                    # Process is not running
87                    print "Clearing shared memory block from lock file\n";
88                    if ( $lines[$#lines] =~ / \A \s* \d+ \s+ \d+ \s* \n* \z /xms ) {
89                        my $out = q{};
90                        for ( my $i = 0; $i < $#lines - 1; $i++ ) {
91                            $out .= $lines[$i];
92                        }
93                        if ( !seek $FILE, 0, 0 ) {
94                            print "Error, seek: $!\n";
95                        }
96                        else {
97                            print $FILE $out;
98                            truncate $FILE, tell($FILE);
99                        }
100                    }
101                }
102            }
103            close $FILE;
104        }
105    }
106
107    system $WAIT4PATH, $data_directory;
108}
109
110exec $postgres_real_path, @postgres_argv;
111exit 0;
112