1/*********************************************************************
2 *
3 * This is based on code created by Peter Harvey,
4 * (pharvey@codebydesign.com).
5 *
6 * Modified and extended by Nick Gorham
7 * (nick@lurcher.org).
8 *
9 * Any bugs or problems should be considered the fault of Nick and not
10 * Peter.
11 *
12 * copyright (c) 1999 Nick Gorham
13 *
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2 of the License, or (at your option) any later version.
18 *
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
23 *
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 *
28 **********************************************************************
29 *
30 * $Id: __stats.c,v 1.4 2009/02/18 17:59:09 lurcher Exp $
31 *
32 * $Log: __stats.c,v $
33 * Revision 1.4 2009/02/18 17:59:09 lurcher
34 * Shift to using config.h, the compile lines were making it hard to spot warnings
35 *
36 * Revision 1.3 2009/02/17 09:47:44 lurcher
37 * Clear up a number of bugs
38 *
39 * Revision 1.2 2004/05/07 09:53:13 lurcher
40 *
41 *
42 * Fix potential problrm in stats if creating a semaphore fails
43 * Alter state after SQLParamData from S4 to S5
44 *
45 * Revision 1.1.1.1 2001/10/17 16:40:09 lurcher
46 *
47 * First upload to SourceForge
48 *
49 * Revision 1.11 2001/09/27 17:05:48 nick
50 *
51 * Assorted fixes and tweeks
52 *
53 * Revision 1.10 2001/06/04 15:24:49 nick
54 *
55 * Add port to MAC OSX and QT3 changes
56 *
57 * Revision 1.9 2001/05/15 13:56:29 jason
58 *
59 * semaphore header file not requires unless COLLECT_STATS is defined
60 *
61 * Revision 1.8 2001/05/15 13:29:07 jason
62 *
63 *
64 * Moved COLLECT_STATS define to allow compilation on OpenVMS.
65 *
66 * Revision 1.7 2001/01/03 10:15:16 martin
67 *
68 * Fix bug in uodbc_update_stats() which attempted to use the shared memory
69 * ID to release the semaphore if the array of process info full.
70 * Fix bug in release_sem_lock() which called semop saying there were 2 ops
71 * when there was really only one.
72 *
73 * Revision 1.6 2000/12/21 16:18:37 martin
74 *
75 * Add the promised support to return a list of process IDs currently attached
76 * to the DM.
77 *
78 * Revision 1.5 2000/12/21 15:58:35 martin
79 *
80 * Fix problems with any app exiting clearing all stats.
81 *
82 * Revision 1.4 2000/12/20 12:00:52 nick
83 *
84 * Add uodbc_update_stats to the non stats build
85 *
86 * Revision 1.3 2000/12/19 10:28:29 martin
87 *
88 * Return "not built with stats" in uodbc_error() if stats function called
89 * when stats not built.
90 * Add uodbc_update_stats() calls to SQLFreeHandle.
91 *
92 * Revision 1.1 2000/12/18 11:53:51 martin
93 *
94 * handle statistic API.
95 *
96 *
97 **********************************************************************/
98
99#include <config.h>
100
101#ifdef HAVE_SYS_SEM_H
102
103#include <stdio.h>
104#include <ctype.h>
105#include <stdlib.h>
106#include <unistd.h>
107#include <string.h>
108#include <errno.h>
109#include <sys/stat.h>
110#include <sys/types.h>
111#endif /* HAVE_SYS_SEM_H */
112#ifdef COLLECT_STATS
113#include <sys/ipc.h>
114#include <sys/sem.h>
115#include <sys/shm.h>
116#endif
117#include <signal.h>
118#include "__stats.h"
119#include <uodbc_stats.h>
120#include "drivermanager.h"
121
122static char const rcsid[]= "$RCSfile: __stats.c,v $ $Revision: 1.4 $";
123
124#ifdef COLLECT_STATS
125#ifdef HAVE_LIBPTHREAD
126
127#include <pthread.h>
128
129#endif
130/*
131 * PROJECT_ID is used in the call to ftok().
132 * The PROJECT_ID reduces the chance of a class between processes using IPC.
133 * Do not change thisnumber as it will make different versions of unixODBC
134 * incompatible.
135 */
136#define PROJECT_ID 121
137
138/*
139 * Permssions on sempahore/shared memory
140 * These needs to be world readable/writeable or different apps running under
141 * different users we not be able to update/read the stats.
142 */
143#define IPC_ACCESS_MODE (S_IRUSR | S_IWUSR \
144 | S_IRGRP | S_IWGRP \
145 | S_IROTH | S_IWOTH)
146
147static char errmsg[512]="";
148
149static int release_sem_lock(int sem_id);
150static int acquire_sem_lock(int sem_id);
151
152
153int uodbc_open_stats(
154 void **rh,
155 unsigned int mode)
156{
157 key_t ipc_key;
158 int shm_created = 0;
159 uodbc_stats_handle_t *h = NULL;
160 uodbc_stats_handle_t lh;
161 char odbcini[1024];
162 unsigned int i;
163 int shmflg;
164
165 if (!rh)
166 {
167 return -1;
168 }
169 if (!_odbcinst_SystemINI(odbcini, FALSE))
170 {
171 snprintf(errmsg, sizeof(errmsg), "Failed to find system odbc.ini");
172 return -1;
173 }
174 memset(&lh, '\0', sizeof(lh));
175 memcpy(lh.id, UODBC_STATS_ID, 5);
176 lh.shm_id = -1;
177 lh.sem_id = -1;
178 lh.pid = getpid();
179
180 /*
181 * Check the odbc.ini file used in ftok() exists.
182 */
183 if (access(odbcini, F_OK) < 0)
184 {
185 snprintf(errmsg, sizeof(errmsg), "Cannot locate %s", odbcini);
186 return -1;
187 }
188
189 /*
190 * Get a unique IPC key.
191 */
192 if ((ipc_key = ftok(odbcini, (char)PROJECT_ID)) < 0)
193 {
194 snprintf(errmsg, sizeof(errmsg),
195 "Failed to obtain IPC key - %s", strerror(errno));
196 return -1;
197 }
198
199 /*
200 * See if the semaphore exists and create if it doesn't.
201 */
202 lh.sem_id = semget(ipc_key, 1, IPC_ACCESS_MODE | IPC_CREAT | IPC_EXCL);
203 if (lh.sem_id < 0)
204 {
205 if (errno != EEXIST)
206 {
207 snprintf(errmsg, sizeof(errmsg),
208 "Failed to get semaphore ID - %s",
209 strerror(errno));
210 return -1;
211 }
212
213 lh.sem_id = semget(ipc_key, 1, IPC_ACCESS_MODE | IPC_CREAT);
214 if (lh.sem_id < 0)
215 {
216 snprintf(errmsg, sizeof(errmsg),
217 "Failed to create semaphore - %s", strerror(errno));
218 return -1;
219 }
220 }
221 /*
222 * Create/map shared memory
223 */
224 if (mode & UODBC_STATS_WRITE)
225 shmflg = IPC_ACCESS_MODE | IPC_CREAT | IPC_EXCL;
226 else
227 shmflg = IPC_ACCESS_MODE;
228
229 lh.shm_id = shmget(ipc_key, sizeof(uodbc_stats_t), shmflg);
230 if (lh.shm_id < 0)
231 {
232 if (mode & UODBC_STATS_READ)
233 {
234 snprintf(errmsg, sizeof(errmsg),
235 "No statistics available yet");
236 return -1;
237 }
238 if (errno == EEXIST)
239 {
240 lh.shm_id = shmget(ipc_key, sizeof(uodbc_stats_t), IPC_ACCESS_MODE);
241 if (lh.shm_id < 0)
242 {
243 snprintf(errmsg, sizeof(errmsg),
244 "Shared memory exists but cannot map it - %s",
245 strerror(errno));
246 return -1;
247 }
248 }
249 else
250 {
251 snprintf(errmsg, sizeof(errmsg),
252 "Failed to get shared memory ID - %s",
253 strerror(errno));
254 return -1;
255 }
256 }
257 else
258 {
259 if (mode & UODBC_STATS_WRITE) shm_created = 1;
260 }
261
262 lh.stats = (uodbc_stats_t *)shmat(lh.shm_id, 0, 0);
263 if (lh.stats == (uodbc_stats_t *)-1)
264 {
265 snprintf(errmsg, sizeof(errmsg),
266 "Failed to attach to shared memory - %s", strerror(errno));
267 return -1;
268 }
269 else if (shm_created)
270 {
271 unsigned int i;
272 int lk;
273
274 lk = acquire_sem_lock(lh.sem_id);
275 memset(lh.stats, '\0', sizeof(uodbc_stats_t));
276 for (i = 0; i < (sizeof(lh.stats->perpid) / sizeof(lh.stats->perpid[0])); i++)
277 {
278 lh.stats->perpid[i].pid = (pid_t)0;
279 }
280 if (lk == 0) release_sem_lock(lh.sem_id);
281 }
282 if ((h = calloc(1, sizeof(uodbc_stats_handle_t))) == NULL) return -1;
283 memcpy(h, &lh, sizeof(uodbc_stats_handle_t));
284 /*
285 * If caller asked for write access it is assumed it is going to
286 * change the statistics and so it needs an entry in the stats.
287 */
288 if (mode & UODBC_STATS_WRITE)
289 {
290 int lk;
291
292 lk = acquire_sem_lock(lh.sem_id);
293 for (i = 0;
294 i < (sizeof(h->stats->perpid) / sizeof(h->stats->perpid[0]));
295 i++)
296 {
297 if (h->stats->perpid[i].pid == (pid_t)0)
298 {
299 h->stats->perpid[i].pid = getpid();
300 h->stats->perpid[i].n_env = 0;
301 h->stats->perpid[i].n_dbc = 0;
302 h->stats->perpid[i].n_stmt = 0;
303 h->stats->perpid[i].n_desc = 0;
304 break;
305 }
306 }
307 if (lk == 0) release_sem_lock(lh.sem_id);
308 }
309
310 *(uodbc_stats_handle_t **)rh = h;
311 return 0;
312}
313
314
315/************************************************************************/
316/* */
317/* uodbc_close_stats */
318/* ================= */
319/* */
320/************************************************************************/
321int uodbc_close_stats(
322 void *h)
323{
324 uodbc_stats_handle_t *sh;
325 sh = (uodbc_stats_handle_t *)h;
326
327 if (!sh)
328 {
329 snprintf(errmsg, sizeof(errmsg), "NULL stats handle");
330 return -1;
331 }
332 if (memcmp(sh->id, UODBC_STATS_ID, sizeof(sh->id)) != 0)
333 {
334 snprintf(errmsg, sizeof(errmsg), "Invalid stats handle %p", sh);
335 return -1;
336 }
337 if ((sh->shm_id != -1) && (sh->stats))
338 {
339 unsigned int i;
340
341 for (i = 0;
342 i < (sizeof(sh->stats->perpid) / sizeof(sh->stats->perpid[0]));
343 i++)
344 {
345 if (sh->stats->perpid[i].pid == sh->pid)
346 {
347 sh->stats->perpid[i].pid = (pid_t) 0;
348 break;
349 }
350 }
351
352 shmdt((char *)sh->stats);
353 sh->stats = NULL;
354 sh->shm_id = -1;
355 }
356 /*
357 * Should we examine attach count and delete shared memory?
358 */
359 memset(sh->id, '\0', sizeof(sh->id));
360 free(sh);
361 return 0;
362}
363
364
365/************************************************************************/
366/* */
367/* uodbc_update_stats */
368/* ================== */
369/* */
370/************************************************************************/
371int uodbc_update_stats(void *h,
372 unsigned int stats_type_mask,
373 void *value)
374{
375 unsigned long type;
376 unsigned int i;
377 uodbc_stats_handle_t *sh;
378 int lk;
379
380 sh = (uodbc_stats_handle_t *)h;
381 if (!sh)
382 {
383 snprintf(errmsg, sizeof(errmsg), "NULL stats handle");
384 return -1;
385 }
386 if (memcmp(sh->id, UODBC_STATS_ID, sizeof(sh->id)) != 0)
387 {
388 snprintf(errmsg, sizeof(errmsg), "Invalid stats handle %p", h);
389 return -1;
390 }
391 if (!sh->stats)
392 {
393 snprintf(errmsg, sizeof(errmsg), "stats memory not mapped");
394 return -1;
395 }
396
397 lk = acquire_sem_lock(sh->sem_id);
398 /*
399 * Find this PID in array
400 */
401 for (i = 0;
402 i < (sizeof(sh->stats->perpid) / sizeof(sh->stats->perpid[0]));
403 i++)
404 {
405 if (sh->stats->perpid[i].pid == sh->pid) break;
406 }
407 /*
408 * Check if array full.
409 */
410 if ( i >= (sizeof(sh->stats->perpid) / sizeof(sh->stats->perpid[0])))
411 {
412 /*
413 * array full - process not entered.
414 */
415 if (lk == 0) release_sem_lock(sh->sem_id);
416 return 0;
417 }
418
419 type = stats_type_mask & UODBC_STATS_TYPE_TYPE_MASK;
420 switch(type)
421 {
422 case UODBC_STATS_TYPE_HENV:
423 {
424 sh->stats->perpid[i].n_env += (long)value;
425 break;
426 }
427 case UODBC_STATS_TYPE_HDBC:
428 {
429 sh->stats->perpid[i].n_dbc += (long)value;
430 break;
431 }
432 case UODBC_STATS_TYPE_HSTMT:
433 {
434 sh->stats->perpid[i].n_stmt += (long)value;
435 break;
436 }
437 case UODBC_STATS_TYPE_HDESC:
438 {
439 sh->stats->perpid[i].n_desc += (long)value;
440 break;
441 }
442 default:
443 {
444 break;
445 }
446 }
447 if (lk == 0) release_sem_lock(sh->sem_id);
448
449 return 0;
450}
451
452/************************************************************************/
453/* */
454/* uodbc_stats_error */
455/* ================= */
456/* */
457/************************************************************************/
458char *uodbc_stats_error(
459 char *buf,
460 size_t buflen)
461{
462 if (!buf) return NULL;
463
464 if (strlen(errmsg) > buflen)
465 {
466 memcpy(buf, errmsg, buflen - 1);
467 buf[buflen - 1] = '\0';
468 }
469 else
470 {
471 strcpy(buf, errmsg);
472 }
473
474 return buf;
475}
476
477
478/************************************************************************/
479/* */
480/* uodbc_get_stats */
481/* =============== */
482/* */
483/* This function should be provided with an array of statistic */
484/* structures which will be filled with the required statistics */
485/* records. */
486/* */
487/* ret_stats = uodbc_get_stats(h, request_pid, s, n_stats); */
488/* */
489/* h = a statistics handle returned from uodbc_open_stats(). */
490/* request_pid = */
491/* -1 = return stats on all attached processes. */
492/* n (n > 0) = return stats on specific process request_pid. */
493/* 0 = return list of processes attached. */
494/* s = ptr to array of statistics structures. */
495/* n_stats = number of statistics structures at s. */
496/* ret_stats = number of stats structures filled in at s. */
497/* */
498/************************************************************************/
499int uodbc_get_stats(
500 void *h,
501 pid_t request_pid,
502 uodbc_stats_retentry *s,
503 int n_stats)
504{
505 uodbc_stats_handle_t *sh;
506 unsigned int i;
507 long n_env=0;
508 long n_dbc=0;
509 long n_stmt=0;
510 long n_desc=0;
511 int cur_stat;
512
513 sh = (uodbc_stats_handle_t *)h;
514
515 if (!sh)
516 {
517 snprintf(errmsg, sizeof(errmsg), "NULL stats return ptr supplied");
518 return -1;
519 }
520 if (n_stats < 1)
521 {
522 snprintf(errmsg, sizeof(errmsg), "No stats return structures supplied");
523 return -1;
524 }
525 if (!sh)
526 {
527 snprintf(errmsg, sizeof(errmsg), "NULL stats handle");
528 return -1;
529 }
530 if (memcmp(sh->id, UODBC_STATS_ID, sizeof(sh->id)) != 0)
531 {
532 snprintf(errmsg, sizeof(errmsg), "Invalid stats handle %p", sh);
533 return -1;
534 }
535 if (!sh->stats)
536 {
537 snprintf(errmsg, sizeof(errmsg), "stats memory not mapped");
538 return -1;
539 }
540 cur_stat = 0;
541 for (i = 0;
542 i < (sizeof(sh->stats->perpid) / sizeof(sh->stats->perpid[0]));
543 i++)
544 {
545 if (sh->stats->perpid[i].pid > 0)
546 {
547 int sts;
548
549 /*
550 * Check this process still exists and if not zero counts.
551 */
552 sts = kill(sh->stats->perpid[i].pid, 0);
553 if ((sts == 0) || ((sts < 0) && (errno == EPERM)))
554 {
555 ;
556 }
557 else
558 {
559 sh->stats->perpid[i].pid = 0;
560 sh->stats->perpid[i].n_env = 0;
561 sh->stats->perpid[i].n_dbc = 0;
562 sh->stats->perpid[i].n_stmt = 0;
563 sh->stats->perpid[i].n_desc = 0;
564 }
565 }
566
567 if (((request_pid == (pid_t)-1) && (sh->stats->perpid[i].pid > 0)) ||
568 (sh->stats->perpid[i].pid == request_pid))
569 {
570 n_env += sh->stats->perpid[i].n_env;
571 n_dbc += sh->stats->perpid[i].n_dbc;
572 n_stmt += sh->stats->perpid[i].n_stmt;
573 n_desc += sh->stats->perpid[i].n_desc;
574 }
575 else if (request_pid == (pid_t)0)
576 {
577 s[cur_stat].type = UODBC_STAT_LONG;
578 s[cur_stat].value.l_value = sh->stats->perpid[i].pid;
579 strcpy(s[cur_stat].name, "PID");
580 if (++cur_stat > n_stats) return cur_stat;
581 }
582 }
583
584 if (request_pid == (pid_t)0) return cur_stat;
585
586 s[cur_stat].type = UODBC_STAT_LONG;
587 s[cur_stat].value.l_value = n_env;
588 strcpy(s[cur_stat].name, "Environments");
589 if (++cur_stat > n_stats) return cur_stat;
590
591 s[cur_stat].type = UODBC_STAT_LONG;
592 s[cur_stat].value.l_value = n_dbc;
593 strcpy(s[cur_stat].name, "Connections");
594 if (++cur_stat > n_stats) return cur_stat;
595
596 s[cur_stat].type = UODBC_STAT_LONG;
597 s[cur_stat].value.l_value = n_stmt;
598 strcpy(s[cur_stat].name, "Statements");
599 if (++cur_stat > n_stats) return cur_stat;
600
601 s[cur_stat].type = UODBC_STAT_LONG;
602 s[cur_stat].value.l_value = n_desc;
603 strcpy(s[cur_stat].name, "Descriptors");
604 if (++cur_stat > n_stats) return cur_stat;
605
606 return cur_stat;
607}
608
609
610/************************************************************************/
611/* */
612/* acquire_sem_lock */
613/* ================ */
614/* */
615/* This function locks other threads/processes out whilst a change to */
616/* to the statistics is made. It uses a global semaphore which was */
617/* created in uodbc_open_stats(). The semaphore set contains only the */
618/* one semaphore which is incremented to 1 when locked. All semops */
619/* are performed with SEM_UNDO so if the process unexepctedly exits */
620/* sempahore operations are undone. */
621/* */
622/* NOTE: some older platforms have a kernel limit on the number of */
623/* SEM_UNDOs structures that may be used. If you run out, you will */
624/* either have to increase the limit or take out the SEM_UNDO in this */
625/* function and release_sem_lock() and hope uodbc_update_stats() never */
626/* causes a preature exit. */
627/* */
628/************************************************************************/
629static int acquire_sem_lock(int sem_id)
630{
631 /*
632 * Semaphore operations:
633 */
634 /* lock the semaphore */
635 struct sembuf op_lock[2] =
636 {
637 {0, 0, 0}, /* Wait for [0] (lock) to equal 0 */
638 {0, 1, SEM_UNDO} /* increment [0] to lock */
639 };
640
641 if (semop(sem_id, &op_lock[0], 2) < 0)
642 {
643 return -1;
644 }
645 return 0;
646}
647
648
649/************************************************************************/
650/* */
651/* release_sem_lock */
652/* ================ */
653/* */
654/* This function unlocks the semaphore used to lock other */
655/* threads/processes out whilst a change to the statistics is made. */
656/* It uses a global semaphore which was created in uodbc_open_stats(). */
657/* The semaphore set contains only the one semaphore which is */
658/* incremented to 1 when locked and decremented when unlocked. */
659/* All semops are performed with SEM_UNDO so if the process */
660/* unexepctedly exits sempahore operations are undone. */
661/* */
662/* NOTE: some older platforms have a kernel limit on the number of */
663/* SEM_UNDOs structures that may be used. If you run out, you will */
664/* either have to increase the limit or take out the SEM_UNDO in this */
665/* function and acquire_sem_lock() and hope uodbc_update_stats() never */
666/* causes a preature exit. */
667/* */
668/************************************************************************/
669static int release_sem_lock(int sem_id)
670{
671 /*
672 * Semaphore operations:
673 */
674 /* unlock the semaphore */
675 struct sembuf op_unlock[1] =
676 {
677 {0, -1, SEM_UNDO}, /* Decrement [0] lock back to zero */
678 };
679
680 if (semop(sem_id, &op_unlock[0], 1) < 0)
681 {
682 return -1;
683 }
684 return 0;
685}
686#else
687int uodbc_open_stats(
688 void **rh,
689 unsigned int mode)
690{
691 return -1;
692}
693int uodbc_close_stats(
694 void *h)
695{
696 return -1;
697}
698char *uodbc_stats_error(
699 char *buf,
700 size_t buflen)
701{
702 const char *notbuilt="unixODBC not built with statistics code";
703
704 if (!buf) return NULL;
705
706 if (strlen(notbuilt) > buflen)
707 {
708 memcpy(buf, notbuilt, buflen - 1);
709 buf[buflen - 1] = '\0';
710 }
711 else
712 {
713 strcpy(buf, notbuilt);
714 }
715
716 return buf;
717}
718int uodbc_get_stats(
719 void *h,
720 pid_t request_pid,
721 uodbc_stats_retentry *s,
722 int n_stats)
723{
724 return -1;
725}
726int uodbc_update_stats(void *h,
727 unsigned int stats_type_mask,
728 void *value)
729{
730 return -1;
731}
732
733#endif /* COLLECT_STATS */
734