source: trunk/base/portmgr/PortIndex2MySQL.tcl @ 29367

Last change on this file since 29367 was 29361, checked in by jmpp@…, 13 years ago

Use the cleanup proc to handle the runlog file too. Call to the subject and recipient variables only if needed.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 11.9 KB
Line 
1#!/usr/bin/env tclsh
2# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:et:sw=4:ts=4:sts=4
3#
4# PortIndex2MySQL.tcl
5# Kevin Van Vechten | kevin@opendarwin.org
6# 3-Oct-2002
7# Juan Manuel Palacios | jmpp@macports.org
8# 30-Jul-2007
9# $Id: PortIndex2MySQL.tcl 29361 2007-09-22 08:16:17Z jmpp@macports.org $
10#
11# Copyright (c) 2007 Juan Manuel Palacios, MacPorts Team.
12# Copyright (c) 2003 Apple Computer, Inc.
13# Copyright (c) 2002 Kevin Van Vechten.
14# All rights reserved.
15#
16# Redistribution and use in source and binary forms, with or without
17# modification, are permitted provided that the following conditions
18# are met:
19# 1. Redistributions of source code must retain the above copyright
20#    notice, this list of conditions and the following disclaimer.
21# 2. Redistributions in binary form must reproduce the above copyright
22#    notice, this list of conditions and the following disclaimer in the
23#    documentation and/or other materials provided with the distribution.
24# 3. Neither the name of Apple Computer, Inc. nor the names of its contributors
25#    may be used to endorse or promote products derived from this software
26#    without specific prior written permission.
27#
28# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
29# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
32# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38# POSSIBILITY OF SUCH DAMAGE.
39
40#####
41# The PortIndex2MySQL script populates a database with key information extracted
42# from the Portfiles in the ports tree pointed to by the sources.conf file in a
43# MacPorts installation, found by loading its macports1.0 tcl package and initializing
44# it with 'mportinit' below. Main use of the resulting database is providing live
45# information to the ports.php page, a client tailored to poll it. For this very reason,
46# information fed to the database always has to be kept up to date in order to remain
47# meaningful, which is accomplished simply by calling 'macports::selfupdate' (which
48# updates the ports tree in use) and by installing the script on cron/launchd to be run
49# on a timely schedule (not any more frequent than the run of the mprsyncup script on
50# the MacPorts server, which is every half hour).
51#
52# Remaining requirement to successfully run this script is performing the necessary
53# MySQL admin tasks on the host box to create the database in the first place and the
54# MySQL user that will be given enough privileges to alter it. Values in the database
55# related variables provided below have to be adapted accordingly to match the chosen
56# setup.
57#####
58
59
60# Load macports1.0 so that we can use some of its procs and the portinfo array.
61catch {source \
62    [file join "@TCL_PACKAGE_DIR@" macports1.0 macports_fastload.tcl]}
63package require macports
64
65
66# Runtime information log file and reciepient.
67set runlog "/tmp/portsdb.log"
68set runlog_fd [open $runlog w+]
69set lockfile "/tmp/portsdb.lock"
70set DATE [clock format [clock seconds] -format "%A %Y-%m-%d at %T"]
71set subject "PortIndex2MySQL run failure on $DATE"
72set SPAM_LOVERS macports-dev@lists.macosforge.org
73
74# House keeping on exit.
75proc cleanup {args} {
76    foreach file_to_clean $args {
77        upvar $file_to_clean up_file_to_clean
78        upvar ${file_to_clean}_fd up_file_to_clean_fd
79        close $up_file_to_clean_fd
80        file delete -force $up_file_to_clean
81    }
82}
83
84# What to do when terminating execution, depending on the $exit_status condition.
85proc terminate {exit_status} {
86    global runlog runlog_fd
87    if {$exit_status} {
88        global subject SPAM_LOVERS
89        seek $runlog_fd 0 start
90        exec -- mail -s $subject $SPAM_LOVERS <@ $runlog_fd
91    }
92    cleanup runlog
93    exit $exit_status
94}
95
96
97# UI instantiation to route information/error messages wherever we want.
98proc ui_channels {priority} {
99    global ui_options runlog_fd
100    switch $priority {
101        debug {
102            if {[macports::ui_isset ui_options ports_debug]} {
103                return $runlog_fd
104            } else {
105                return {}
106            }
107        }
108        info {
109            if {[macports::ui_isset ui_options ports_verbose]} {
110                return $runlog_fd
111            } else {
112                return {}
113            }
114        }
115        msg {
116            if {[macports::ui_isset ui_options ports_quiet]} {
117                return $runlog_fd
118            } else {
119                return {}
120            }
121        }
122        error {
123            return $runlog_fd
124        }
125        default {
126            return {}
127        }
128    }
129}
130
131
132# Check if there are any stray sibling jobs before moving on, bail in such case.
133if {[file exists $lockfile]} {
134    ui_error "PortIndex2MySQL lock file found, is another job running?"
135    terminate 1
136} else {
137    set lockfile_fd [open $lockfile a]
138}
139
140
141# Initialize macports1.0 and its UI, in order to find the sources.conf file
142# (which is what will point us to the PortIndex we're gonna use) and use
143# the runtime information.
144array set ui_options {ports_verbose yes}
145if {[catch {mportinit ui_options} errstr]} {
146    ui_error "${::errorInfo}"
147    ui_error "Failed to initialize MacPorts, $errstr"
148    cleanup lockfile
149    terminate 1
150}
151
152# Call the selfupdate procedure to make sure the MacPorts installation
153# is up-to-date and with a fresh ports tree.
154if {[catch {macports::selfupdate} errstr]} {
155    ui_error "${::errorInfo}"
156    ui_error "Failed to update the ports tree, $errstr"
157    cleanup lockfile
158    terminate 1
159}
160
161
162# Procedure to catch the database password from a protected file.
163proc getpasswd {passwdfile} {
164    if {[catch {open $passwdfile r} passwdfile_fd]} {
165        ui_error "${::errorCode}: $passwdfile_fd"
166        cleanup lockfile
167        terminate 1
168    }
169    if {[gets $passwdfile_fd passwd] <= 0} {
170        close $passwdfile_fd
171        ui_error "No password found in $passwdfile!"
172        cleanup lockfile
173        terminate 1
174    }
175    close $passwdfile_fd
176    return $passwd
177}
178
179# Database abstraction variables:
180set sqlfile "/tmp/portsdb.sql"
181set dbcmd [macports::findBinary mysql5]
182set dbhost 127.0.0.1
183set dbuser macports
184set passwdfile "./password_file"
185set dbpasswd [getpasswd $passwdfile]
186set dbname macports_ports
187
188
189# Flat text file to which sql statements are written.
190if {[catch {open $sqlfile w+} sqlfile_fd]} {
191    ui_error "${::errorCode}: $sqlfile_fd"
192    cleanup lockfile
193    terminate 1
194}
195
196# SQL string escaping.
197proc sql_escape {str} {
198    regsub -all -- {'} $str {\\'} str
199    regsub -all -- {"} $str {\\"} str
200    regsub -all -- {\n} $str {\\n} str
201    return $str
202}
203
204# Initial creation of database tables: log, portfiles, categories, maintainers, dependencies, variants and platforms.
205# Do we need any other?
206puts $sqlfile_fd "DROP TABLE IF EXISTS log;"
207puts $sqlfile_fd "CREATE TABLE IF NOT EXISTS log (activity VARCHAR(255), activity_time TIMESTAMP(14));"
208puts $sqlfile_fd "INSERT INTO log VALUES ('update', NOW());"
209
210puts $sqlfile_fd "DROP TABLE IF EXISTS portfiles;"
211puts $sqlfile_fd "CREATE TABLE portfiles (name VARCHAR(255) PRIMARY KEY NOT NULL, path VARCHAR(255), version VARCHAR(255),  description TEXT);"
212
213puts $sqlfile_fd "DROP TABLE IF EXISTS categories;"
214puts $sqlfile_fd "CREATE TABLE categories (portfile VARCHAR(255), category VARCHAR(255), is_primary INTEGER);"
215
216puts $sqlfile_fd "DROP TABLE IF EXISTS maintainers;"
217puts $sqlfile_fd "CREATE TABLE maintainers (portfile VARCHAR(255), maintainer VARCHAR(255), is_primary INTEGER);"
218
219puts $sqlfile_fd "DROP TABLE IF EXISTS dependencies;"
220puts $sqlfile_fd "CREATE TABLE dependencies (portfile VARCHAR(255), library VARCHAR(255));"
221
222puts $sqlfile_fd "DROP TABLE IF EXISTS variants;"
223puts $sqlfile_fd "CREATE TABLE variants (portfile VARCHAR(255), variant VARCHAR(255));"
224
225puts $sqlfile_fd "DROP TABLE IF EXISTS platforms;"
226puts $sqlfile_fd "CREATE TABLE platforms (portfile VARCHAR(255), platform VARCHAR(255));"
227
228
229# Load every port in the index through a search that matches everything.
230if {[catch {set ports [mportsearch ".+"]} errstr]} {
231    ui_error "${::errorInfo}"
232    ui_error "port search failed: $errstr"
233    cleanup sqlfile lockfile
234    terminate 1
235}
236
237# Iterate over each matching port, extracting its information from the
238# portinfo array.
239foreach {name array} $ports {
240
241    array unset portinfo
242    array set portinfo $array
243
244    set portname [sql_escape $portinfo(name)]
245    if {[info exists portinfo(version)]} {
246        set portversion [sql_escape $portinfo(version)]
247    } else {
248        set portversion ""
249    }
250    set portdir [sql_escape $portinfo(portdir)]
251    if {[info exists portinfo(description)]} {
252        set description [sql_escape $portinfo(description)]
253    } else {
254        set description ""
255    }
256    if {[info exists portinfo(categories)]} {
257        set categories $portinfo(categories)
258    } else {
259        set categories ""
260    }
261    if {[info exists portinfo(maintainers)]} {
262        set maintainers $portinfo(maintainers)
263    } else {
264        set maintainers ""
265    }
266    if {[info exists portinfo(variants)]} {
267        set variants $portinfo(variants)
268    } else {
269        set variants ""
270    }
271    if {[info exists portinfo(depends_build)]} {
272        set depends_build $portinfo(depends_build)
273    } else {
274        set depends_build ""
275    }
276    if {[info exists portinfo(depends_lib)]} {
277        set depends_lib $portinfo(depends_lib)
278    } else {
279        set depends_lib ""
280    }
281    if {[info exists portinfo(depends_run)]} {
282        set depends_run $portinfo(depends_run)
283    } else {
284        set depends_run ""
285    }
286    if {[info exists portinfo(platforms)]} {
287        set platforms $portinfo(platforms)
288    } else {
289        set platforms ""
290    }
291
292    puts $sqlfile_fd "INSERT INTO portfiles VALUES ('$portname', '$portdir', '$portversion', '$description');"
293
294    set primary 1
295    foreach category $categories {
296        set category [sql_escape $category]
297        puts $sqlfile_fd "INSERT INTO categories VALUES ('$portname', '$category', $primary);"
298        incr primary
299    }
300   
301    set primary 1
302    foreach maintainer $maintainers {
303        set maintainer [sql_escape $maintainer]
304        puts $sqlfile_fd "INSERT INTO maintainers VALUES ('$portname', '$maintainer', $primary);"
305        incr primary
306    }
307
308    foreach build_dep $depends_build {
309        set build_dep [sql_escape $build_dep]
310        puts $sqlfile_fd "INSERT INTO dependencies VALUES ('$portname', '$build_dep');"
311    }
312
313    foreach lib $depends_lib {
314        set lib [sql_escape $lib]
315        puts $sqlfile_fd "INSERT INTO dependencies VALUES ('$portname', '$lib');"
316    }
317
318    foreach run_dep $depends_run {
319        set run_dep [sql_escape $run_dep]
320        puts $sqlfile_fd "INSERT INTO dependencies VALUES ('$portname', '$run_dep');"
321    }
322
323    foreach variant $variants {
324        set variant [sql_escape $variant]
325        puts $sqlfile_fd "INSERT INTO variants VALUES ('$portname', '$variant');"
326    }
327
328    foreach platform $platforms {
329        set platform [sql_escape $platform]
330        puts $sqlfile_fd "INSERT INTO platforms VALUES ('$portname', '$platform');"
331    }
332
333}
334
335
336# Pipe the contents of the generated sql file to the database command,
337# reading from the file descriptor for the raw sql file to assure completeness.
338if {[catch {seek $sqlfile_fd 0 start} errstr]} {
339    ui_error "${::errorCode}: $errstr"
340    cleanup sqlfile lockfile
341    terminate 1
342}
343if {[catch {exec -- $dbcmd --host=$dbhost --user=$dbuser --password=$dbpasswd --database=$dbname <@ $sqlfile_fd} errstr]} {
344    ui_error "${::errorCode}: $errstr"
345    cleanup sqlfile lockfile
346    terminate 1
347}
348
349
350# And we're done regen'ing the MacPorts dabase! Cleanup and exit successfully.
351cleanup sqlfile lockfile
352terminate 0
Note: See TracBrowser for help on using the repository browser.