actually shows something whee
[snqmon:snqmon.git] / monutils.php
1 <?php
2 /**
3  * StatusNet - the distributed open-source microblogging tool
4  * Copyright (C) 2010 StatusNet, Inc.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  * @package snqmon
20  * @author Brion Vibber <brion@status.net>
21  */
22
23 /**
24  * Internal holder for processing thread status info
25  */
26 class ThreadState
27 {
28     public $host;
29     public $pid;
30     public $state;
31     public $stats=array();
32     
33     function __construct($row)
34     {
35         $this->host = $row['host'];
36         $this->pid = intval($row['pid']);
37         $this->state = $row['state'];
38         // @fixme stats
39     }
40
41     function stat($key)
42     {
43         if (isset($this->stats[$key])) {
44             return $this->stats[$key];
45         } else {
46             return 0;
47         }
48     }
49 }
50
51 function showThreads($threads)
52 {
53     $threads = sortThreads($threads);
54     $cells = array();
55     foreach ($threads as $thread) {
56         $cells[] = "<td class='state-{$thread->state}'>" .
57                    "<div class='id'>{$thread->host}.{$thread->pid}</div>" .
58                    "<div class='state'>{$thread->state}</div>" .
59                    "<div class='count'>{$thread->stat('handled')} handled</div>" .
60                    "<div class='count'>{$thread->stat('switch')} switches</div>" .
61                    "</td>";
62     }
63     columnTable($cells, 8, 'threads');
64 }
65
66 function sortThreads($threads)
67 {
68     usort($threads, 'sortThreadsCallback');
69     return $threads;
70 }
71
72 function sortThreadsCallback($a, $b)
73 {
74     if ($a->host == $b->host) {
75         return strnatcmp($a->pid, $b->pid);
76     } else {
77         return strnatcmp($a->host, $b->host);
78     }
79 }
80
81 /**
82  * Outputs a table with the given cells, broken into rows
83  * with the given number of columns.
84  *
85  * @param array $cells array of <td>...</td> HTML fragments
86  * @param int $cols number of columns to break into
87  * @param string $class optional CSS class for the table
88  */
89 function columnTable($cells, $cols, $class='')
90 {
91     print "<table class='$class'>";
92     for ($i = 0; $i < count($cells); $i += $cols) {
93         print "<tr>";
94         foreach (array_slice($cells, $i, 8) as $cell) {
95             print $cell;
96         }
97         print "</tr>";
98     }
99     print "</table>";
100 }
101
102
103
104 class SnqMon
105 {
106     protected $db;
107
108     function connect()
109     {
110         if (empty($this->db)) {
111             $this->db = new SQLiteDatabase('data/snqmon-threads', 0666);
112         }
113     }
114     
115     function setup()
116     {
117         $this->connect();
118         $this->db->query("CREATE TABLE threads (" .
119             "host," .
120             "pid INTEGER," .
121             "state," .
122             "touched DATETIME)");
123     }
124     
125     function saveThreadState($thread)
126     {
127         $this->connect();
128
129         $this->db->queryExec('BEGIN');
130
131         $sql = "UPDATE threads SET state='%s',touched=datetime('now') WHERE host='%s' AND pid=%d";
132         $ok = $this->db->queryExec(sprintf($sql, sqlite_escape_string($thread->state),
133                                       sqlite_escape_string($thread->host),
134                                       $thread->pid));
135         if ($ok) {
136             if ($this->db->changes() == 0) {
137                 $sql = "INSERT INTO threads (host,pid,state,touched) VALUES ('%s',%d,'%s',datetime('now'))";
138                 $ok = $this->db->queryExec(sprintf($sql, sqlite_escape_string($thread->host),
139                                               $thread->pid,
140                                               sqlite_escape_string($thread->state)));
141             }
142         }
143
144         if ($ok) {
145             $this->db->queryExec('COMMIT');
146         } else {
147             $this->db->queryExec('ROLLBACK');
148         }
149
150         return $ok;
151     }
152
153     function getThreads()
154     {
155         $this->connect();
156         $query = "SELECT host, pid, state FROM threads";
157         $data = $this->db->arrayQuery($query);
158         return $this->buildState($data);
159     }
160     
161     function buildState($data)
162     {
163         $threads = array();
164         foreach ($data as $row) {
165             $threads[] = new ThreadState($row);
166         }
167         return $threads;
168     }
169
170     function demoThreads()
171     {
172         $data = array(array('host' => 'localhost',
173                             'pid' => 123,
174                             'state' => 'init'),
175                       array('host' => 'localhost',
176                             'pid' => 34,
177                             'state' => 'listening'),
178                       array('host' => 'localhost',
179                             'pid' => 345,
180                             'state' => 'queue'),
181                       array('host' => 'localhost',
182                             'pid' => 123,
183                             'state' => 'xmpp'),
184                       array('host' => 'idlehost',
185                             'pid' => 123,
186                             'state' => 'idle'));
187         return $this->buildState($data);
188     }
189 }
190