1
#!/usr/bin/env php
2
<?php
3
/*
4
 * StatusNet - the distributed open-source microblogging tool
5
 * Copyright (C) 2008, 2009, StatusNet, Inc.
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Affero General Public License as published by
9
 * the Free Software Foundation, either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
22
23
$shortoptions = 'fi:at:';
24
$longoptions = array('id=', 'foreground', 'all', 'threads=');
25
26
/**
27
 * Attempts to get a count of the processors available on the current system
28
 * to fan out multiple threads.
29
 *
30
 * Recognizes Linux and Mac OS X; others will return default of 1.
31
 *
32
 * @fixme move this to SpawningDaemon, but to get the default val for help
33
 *        text we seem to need it before loading infrastructure
34
 * @return intval
35
 */
36
function getProcessorCount()
37
{
38
    $cpus = 0;
39
    switch (PHP_OS) {
40
    case 'Linux':
41
        $cpuinfo = file('/proc/cpuinfo');
42
        foreach (file('/proc/cpuinfo') as $line) {
43
            if (preg_match('/^processor\s+:\s+(\d+)\s?$/', $line)) {
44
                $cpus++;
45
            }
46
        }
47
        break;
48
    case 'Darwin':
49
        $cpus = intval(shell_exec("/usr/sbin/sysctl -n hw.ncpu 2>/dev/null"));
50
        break;
51
    }
52
    if ($cpus) {
53
        return $cpus;
54
    }
55
    return 1;
56
}
57
58
$threads = getProcessorCount();
59
$helptext = <<<END_OF_QUEUE_HELP
60
Daemon script for running queued items.
61
62
    -i --id           Identity (default none)
63
    -f --foreground   Stay in the foreground (default background)
64
    -a --all          Handle queues for all local sites
65
                      (requires Stomp queue handler, status_network setup)
66
    -t --threads=<n>  Spawn <n> processing threads (default $threads)
67
68
69
END_OF_QUEUE_HELP;
70
71
require_once INSTALLDIR.'/scripts/commandline.inc';
72
73
require_once(INSTALLDIR.'/lib/daemon.php');
74
require_once(INSTALLDIR.'/classes/Queue_item.php');
75
require_once(INSTALLDIR.'/classes/Notice.php');
76
77
/**
78
 * Queue handling daemon...
79
 *
80
 * The queue daemon by default launches in the background, at which point
81
 * it'll pass control to the configured QueueManager class to poll for updates.
82
 *
83
 * We can then pass individual items through the QueueHandler subclasses
84
 * they belong to.
85
 */
86
class QueueDaemon extends SpawningDaemon
87
{
88
    protected $allsites = false;
89
90
    function __construct($id=null, $daemonize=true, $threads=1, $allsites=false)
91
    {
92
        parent::__construct($id, $daemonize, $threads);
93
        $this->allsites = $allsites;
94
    }
95
96
    /**
97
     * Setup and start of run loop for this queue handler as a daemon.
98
     * Most of the heavy lifting is passed on to the QueueManager's service()
99
     * method, which passes control on to the QueueHandler's handle()
100
     * method for each item that comes in on the queue.
101
     *
102
     * @return boolean true on success, false on failure
103
     */
104
    function runThread()
105
    {
106
        $this->log(LOG_INFO, 'checking for queued notices');
107
108
        $master = new QueueMaster($this->get_id(), $this->processManager());
109
        $master->init($this->allsites);
110
        try {
111
            $master->service();
112
        } catch (Exception $e) {
113
            common_log(LOG_ERR, "Unhandled exception: " . $e->getMessage() . ' ' .
114
                str_replace("\n", " ", $e->getTraceAsString()));
115
            return self::EXIT_ERR;
116
        }
117
118
        $this->log(LOG_INFO, 'finished servicing the queue');
119
120
        $this->log(LOG_INFO, 'terminating normally');
121
122
        return $master->respawn ? self::EXIT_RESTART : self::EXIT_SHUTDOWN;
123
    }
124
}
125
126
class QueueMaster extends IoMaster
127
{
128
    protected $processManager;
129
130
    function __construct($id, $processManager)
131
    {
132
        parent::__construct($id);
133
        $this->processManager = $processManager;
134
    }
135
136
    /**
137
     * Initialize IoManagers which are appropriate to this instance.
138
     */
139
    function initManagers()
140
    {
141
        $managers = array();
142
        if (Event::handle('StartQueueDaemonIoManagers', array(&$managers))) {
143
            $qm = QueueManager::get();
144
            $qm->setActiveGroup('main');
145
            $managers[] = $qm;
146
            $managers[] = $this->processManager;
147
        }
148
        Event::handle('EndQueueDaemonIoManagers', array(&$managers));
149
150
        foreach ($managers as $manager) {
151
            $this->instantiate($manager);
152
        }
153
    }
154
}
155
156
if (have_option('i')) {
157
    $id = get_option_value('i');
158
} else if (have_option('--id')) {
159
    $id = get_option_value('--id');
160
} else if (count($args) > 0) {
161
    $id = $args[0];
162
} else {
163
    $id = null;
164
}
165
166
if (have_option('t')) {
167
    $threads = intval(get_option_value('t'));
168
} else if (have_option('--threads')) {
169
    $threads = intval(get_option_value('--threads'));
170
} else {
171
    $threads = 0;
172
}
173
if (!$threads) {
174
    $threads = getProcessorCount();
175
}
176
177
$daemonize = !(have_option('f') || have_option('--foreground'));
178
$all = have_option('a') || have_option('--all');
179
180
$daemon = new QueueDaemon($id, $daemonize, $threads, $all);
181
$daemon->runOnce();