forked from bloomberg/comdb2
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcdb2api.c
More file actions
9438 lines (8435 loc) · 324 KB
/
cdb2api.c
File metadata and controls
9438 lines (8435 loc) · 324 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
Copyright 2015, 2023, Bloomberg Finance L.P.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <inttypes.h>
#include <alloca.h>
#include <stdarg.h>
#include <comdb2buf.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/queue.h>
#include <fcntl.h>
#include <resolv.h>
#include <math.h> // ceil
#include <limits.h> // int_max
#include "cdb2api.h"
#include "cdb2api_hndl.h"
#include "cdb2_identity.h"
#include "cdb2api_int.h"
#include "sqlquery.pb-c.h"
#include "sqlresponse.pb-c.h"
#include "str_util.h" /* QUOTE */
#ifndef API_DRIVER_NAME
#define API_DRIVER_NAME open_cdb2api
#endif
static char api_driver_name[] = QUOTE(API_DRIVER_NAME);
#ifndef API_DRIVER_VERSION
#define API_DRIVER_VERSION latest
#endif
static char api_driver_version[] = QUOTE(API_DRIVER_VERSION);
#define SOCKPOOL_SOCKET_NAME "/tmp/sockpool.socket"
#define COMDB2DB "comdb2db"
#define COMDB2DB_NUM 32432
#define COMDB2DB_DEV "comdb3db"
#define COMDB2DB_DEV_NUM 192779
#define MAX_BUFSIZE_ONSTACK 8192
static char COMDB2DB_OVERRIDE[32] = {0};
static int COMDB2DB_NUM_OVERRIDE = 0;
static char *SOCKPOOL_OTHER_NAME = NULL;
static char CDB2DBCONFIG_NOBBENV[512] = "/opt/bb/etc/cdb2/config/comdb2db.cfg";
/* Per-database additional config path */
static char CDB2DBCONFIG_ADDL_PATH[512] = "";
/* The real path is COMDB2_ROOT + CDB2DBCONFIG_NOBBENV_PATH */
static char CDB2DBCONFIG_NOBBENV_PATH[] = "/etc/cdb2/config.d/"; /* READ-ONLY */
static char CDB2DBCONFIG_PKG_PATH[64] = "/bb/etc/cdb2/config.d/";
static char CDB2DBCONFIG_TEMP_BB_BIN[512] = "/bb/bin/comdb2db.cfg";
static char *CDB2DBCONFIG_BUF = NULL;
static int have_read_env = 0;
static char cdb2_default_cluster[64] = "";
static int cdb2_default_cluster_set_from_env = 0;
static char cdb2_comdb2dbname[32] = "";
static int cdb2_comdb2dbname_set_from_env = 0;
#ifndef CDB2_DNS_SUFFIX
#define CDB2_DNS_SUFFIX
#endif
static char cdb2_dnssuffix[255] = QUOTE(CDB2_DNS_SUFFIX);
static int cdb2_dnssuffix_set_from_env = 0;
#ifndef CDB2_BMS_SUFFIX
#define CDB2_BMS_SUFFIX
#endif
static char cdb2_bmssuffix[128] = QUOTE(CDB2_BMS_SUFFIX);
static int cdb2_bmssuffix_set_from_env = 0;
static char cdb2_machine_room[16] = "";
static int cdb2_machine_room_set_from_env = 0;
static int CDB2_PORTMUXPORT = 5105;
static int cdb2_portmuxport_set_from_env = 0;
static int MAX_RETRIES = 20; /* We are looping each node twice. */
static int MIN_RETRIES = 16;
static int CDB2_CONNECT_TIMEOUT = 100;
static int cdb2_connect_timeout_set_from_env = 0;
#ifdef CDB2API_SERVER
static int CDB2_MAX_AUTO_CONSUME_ROWS = 10;
#endif
static int COMDB2DB_TIMEOUT = 2000;
static int cdb2_comdb2db_timeout_set_from_env = 0;
static int CDB2_API_CALL_TIMEOUT = 120000; /* defaults to 2 minute */
static int cdb2_api_call_timeout_set_from_env = 0;
static int CDB2_ENFORCE_API_CALL_TIMEOUT = 0;
static int cdb2_enforce_api_call_timeout_set_from_env = 0;
static int CDB2_SOCKET_TIMEOUT = 5000;
static int cdb2_socket_timeout_set_from_env = 0;
static int CDB2_SOCKPOOL_SEND_TIMEOUTMS = 0;
static int cdb2_sockpool_send_timeoutms_set_from_env = 0;
static int CDB2_SOCKPOOL_RECV_TIMEOUTMS = 0;
static int cdb2_sockpool_recv_timeoutms_set_from_env = 0;
static int CDB2_POLL_TIMEOUT = 250;
static int cdb2_tcpbufsz = 0;
static int cdb2_tcpbufsz_set_from_env = 0;
static int CDB2_PROTOBUF_SIZE = 0;
static int cdb2_protobuf_size_set_from_env = 0;
static int cdb2_use_bmsd = 1;
static int cdb2_use_bmsd_set_from_env = 0;
static int cdb2_comdb2db_fallback = 1;
static int cdb2_comdb2db_fallback_set_from_env = 0;
static int cdb2_use_optional_identity = 1;
static int cdb2_use_optional_identity_set_from_env = 0;
static int cdb2_discard_unread_socket_data = 0;
static int cdb2_discard_unread_socket_data_set_from_env = 0;
static int cdb2_alarm_unread_socket_data = 0;
static int cdb2_alarm_unread_socket_data_set_from_env = 0;
static int cdb2_max_discard_records = 1;
static int cdb2_max_discard_records_set_from_env = 0;
/* flattens column values in protobuf structure */
#ifdef CDB2_LEGACY_DEFAULTS
static int cdb2_flat_col_vals = 0;
#else
static int cdb2_flat_col_vals = 1;
#endif
static int cdb2_flat_col_vals_set_from_env = 0;
/* estimates how much memory protobuf will need, and pre-allocates that much */
static int CDB2_PROTOBUF_HEURISTIC_INIT_SIZE = 1024;
#ifdef CDB2_LEGACY_DEFAULTS
static int cdb2_protobuf_heuristic = 0;
#else
static int cdb2_protobuf_heuristic = 1;
#endif
static int cdb2_protobuf_heuristic_set_from_env = 0;
static int cdb2_non_threaded_identity = 0;
static int cdb2_non_threaded_identity_from_env = 0;
static int CDB2_REQUEST_FP = 0;
static int log_calls = 0;
#define LOG_CALL(fmt, ...) \
do { \
if (log_calls) { \
fprintf(stderr, "%d [pid %d tid %p]> " fmt, (int)time(NULL), getpid(), (void *)pthread_self(), \
##__VA_ARGS__); \
} \
} while (0)
static void *cdb2_protobuf_alloc(void *allocator_data, size_t size)
{
struct cdb2_hndl *hndl = allocator_data;
void *p = NULL;
if (hndl->protobuf_data && (size <= hndl->protobuf_size - hndl->protobuf_offset)) {
p = hndl->protobuf_data + hndl->protobuf_offset;
hndl->protobuf_offset += ((size + 7) & ~7);
if (hndl->protobuf_offset > hndl->protobuf_size)
hndl->protobuf_offset = hndl->protobuf_size;
LOG_CALL("%s: got %zu bytes from pool max %d current %d\n", __func__, size, hndl->protobuf_size,
hndl->protobuf_offset);
} else {
LOG_CALL("%s: malloc(%zu) max %d current %d\n", __func__, size, hndl->protobuf_size, hndl->protobuf_offset);
p = malloc(size);
}
return p;
}
void cdb2_protobuf_free(void *allocator_data, void *ptr)
{
struct cdb2_hndl *hndl = allocator_data;
char *start = hndl->protobuf_data;
char *end = start + hndl->protobuf_size;
char *p = ptr;
if (hndl->protobuf_data == NULL || p < start || p >= end) {
LOG_CALL("%s: calling system free\n", __func__);
free(p);
}
}
#include <openssl/conf.h>
#include <openssl/crypto.h>
struct local_cached_connection {
union {
int fd;
COMDB2BUF *sb;
};
char typestr[TYPESTR_LEN];
time_t when;
TAILQ_ENTRY(local_cached_connection) local_cache_lnk;
};
static pthread_mutex_t local_connection_cache_lock = PTHREAD_MUTEX_INITIALIZER;
static int max_local_connection_cache_entries = 0;
static int max_local_connection_cache_entries_envvar = 0;
static int max_local_connection_cache_age = 10;
static int max_local_connection_cache_age_envvar = 0;
static int local_connection_cache_use_sbuf = 0;
static int local_connection_cache_use_sbuf_envvar = 0;
static int local_connection_cache_check_pid = 0;
static int local_connection_cache_check_pid_envvar = 0;
static int connection_cache_entries = 0;
int cdb2_use_ftruncate = 0;
static int cdb2_use_ftruncate_set_from_env = 0;
static int cdb2_use_env_vars = 1;
static int cdb2_install_set_from_env = 0;
TAILQ_HEAD(local_connection_cache_list, local_cached_connection);
/* owner of the in-proc connection cache */
static pid_t local_connection_cache_owner_pid;
struct local_connection_cache_list local_connection_cache;
struct local_connection_cache_list free_local_connection_cache;
typedef struct local_connection_cache_list local_connection_cache_list;
static int cdb2cfg_override = 0;
static int default_type_override_env = 0;
/* Each feature needs to be enabled by default */
static int connect_host_on_reject = 1;
static int cdb2_connect_host_on_reject_set_from_env = 0;
static int iam_identity = 1; /* on by default */
static int cdb2_iam_identity_set_from_env = 0;
static int donate_unused_connections = 1; /* on by default */
static int cdb2_donate_unused_connections_set_from_env = 0;
/* Fix last set repeat, disable if this breaks anything */
static int disable_fix_last_set = 0;
static int cdb2_disable_fix_last_set_set_from_env = 0;
/* Skip dbinfo query if sockpool provides connection */
static int get_dbinfo = 0;
static int cdb2_get_dbinfo_set_from_env = 0;
/* Request host name of a connection obtained from sockpool */
static int get_hostname_from_sockpool_fd = 0;
static int cdb2_get_hostname_from_sockpool_fd_set_from_env = 0;
#define CDB2_ALLOW_PMUX_ROUTE_DEFAULT 0
static int cdb2_allow_pmux_route = CDB2_ALLOW_PMUX_ROUTE_DEFAULT;
static int cdb2_allow_pmux_route_set_from_env = 0;
static int retry_dbinfo_on_cached_connection_failure = 1;
static int cdb2_retry_dbinfo_on_cached_connection_failure_set_from_env = 0;
/* ssl client mode */
static ssl_mode cdb2_c_ssl_mode = SSL_ALLOW;
/* path to ssl certificate directory. searches 'client.crt', 'client.key', 'root.crt'
and 'root.crl' for certificate, key, CA certificate and revocation list, respectively */
static char cdb2_sslcertpath[PATH_MAX];
/* path to ssl certificate. overrides cdb2_sslcertpath if specified */
static char cdb2_sslcert[PATH_MAX];
/* path to ssl key. overrides cdb2_sslcertpath if specified */
static char cdb2_sslkey[PATH_MAX];
/* path to CA. overrides cdb2_sslcertpath if specified */
static char cdb2_sslca[PATH_MAX];
#if HAVE_CRL
/* path to CRL. overrides cdb2_sslcertpath if specified */
static char cdb2_sslcrl[PATH_MAX];
#endif
/* specify which NID in server certificate represents the database name */
#ifdef NID_host /* available as of RFC 4524 */
#define CDB2_NID_DBNAME_DEFAULT NID_host
#else
#define CDB2_NID_DBNAME_DEFAULT NID_commonName
#endif
int cdb2_nid_dbname = CDB2_NID_DBNAME_DEFAULT;
static int cdb2_cache_ssl_sess = 0;
static double cdb2_min_tls_ver = 0;
static int _PID; /* ONE-TIME */
static int _MACHINE_ID; /* ONE-TIME */
static char *_ARGV0; /* ONE-TIME */
#define DB_TZNAME_DEFAULT "America/New_York"
static pthread_mutex_t cdb2_event_mutex = PTHREAD_MUTEX_INITIALIZER;
static cdb2_event cdb2_gbl_events;
static int cdb2_gbl_event_version;
static cdb2_event *cdb2_next_callback(cdb2_hndl_tp *, cdb2_event_type, cdb2_event *);
static void *cdb2_invoke_callback(cdb2_hndl_tp *, cdb2_event *, int, ...);
static int refresh_gbl_events_on_hndl(cdb2_hndl_tp *);
static int cdb2_get_dbhosts(cdb2_hndl_tp *);
static void hndl_set_comdb2buf(cdb2_hndl_tp *, COMDB2BUF *, int idx);
static int send_reset(COMDB2BUF *sb, int localcache);
static void free_raw_response(cdb2_hndl_tp *hndl);
static int check_hb_on_blocked_write = 0; // temporary switch - this will be default behavior
static int check_hb_on_blocked_write_set_from_env = 0;
static pthread_mutex_t cdb2_ssl_sess_lock = PTHREAD_MUTEX_INITIALIZER;
static cdb2_ssl_sess *cdb2_get_ssl_sessions(cdb2_hndl_tp *hndl);
static int cdb2_set_ssl_sessions(cdb2_hndl_tp *hndl,
cdb2_ssl_sess *sessions);
static int cdb2_add_ssl_session(cdb2_hndl_tp *hndl);
static pthread_mutex_t cdb2_cfg_lock = PTHREAD_MUTEX_INITIALIZER;
#ifdef CDB2API_TEST
#include "cdb2api_test.h"
#include "cdb2api_ssl_test.h"
#define MAKE_CDB2API_TEST_SWITCH(name) \
static int name; \
void set_##name(int num) \
{ \
name = num; \
}
MAKE_CDB2API_TEST_SWITCH(fail_dbhosts_invalid_response)
MAKE_CDB2API_TEST_SWITCH(fail_dbhosts_bad_response)
MAKE_CDB2API_TEST_SWITCH(fail_dbhosts_cant_read_response)
MAKE_CDB2API_TEST_SWITCH(fail_dbinfo_invalid_header)
MAKE_CDB2API_TEST_SWITCH(fail_dbinfo_invalid_response)
MAKE_CDB2API_TEST_SWITCH(fail_dbinfo_no_response)
MAKE_CDB2API_TEST_SWITCH(fail_next)
MAKE_CDB2API_TEST_SWITCH(fail_read)
MAKE_CDB2API_TEST_SWITCH(fail_reject)
MAKE_CDB2API_TEST_SWITCH(fail_sb)
MAKE_CDB2API_TEST_SWITCH(fail_send)
MAKE_CDB2API_TEST_SWITCH(fail_sockpool)
MAKE_CDB2API_TEST_SWITCH(fail_tcp)
MAKE_CDB2API_TEST_SWITCH(fail_timeout_sockpool_recv)
MAKE_CDB2API_TEST_SWITCH(fail_timeout_sockpool_send)
MAKE_CDB2API_TEST_SWITCH(fail_ssl_negotiation_once)
MAKE_CDB2API_TEST_SWITCH(fail_sslio_close_in_local_cache)
#define MAKE_CDB2API_TEST_COUNTER(name) \
static int name; \
int get_##name(void) \
{ \
return name; \
}
MAKE_CDB2API_TEST_COUNTER(num_get_dbhosts)
MAKE_CDB2API_TEST_COUNTER(num_skip_dbinfo)
MAKE_CDB2API_TEST_COUNTER(num_sockpool_fd)
MAKE_CDB2API_TEST_COUNTER(num_sql_connects)
MAKE_CDB2API_TEST_COUNTER(num_tcp_connects)
MAKE_CDB2API_TEST_COUNTER(num_cache_lru_evicts)
MAKE_CDB2API_TEST_COUNTER(num_cache_hits)
MAKE_CDB2API_TEST_COUNTER(num_cache_misses)
MAKE_CDB2API_TEST_COUNTER(num_sockpool_recv)
MAKE_CDB2API_TEST_COUNTER(num_sockpool_send)
MAKE_CDB2API_TEST_COUNTER(num_sockpool_recv_timeouts)
MAKE_CDB2API_TEST_COUNTER(num_sockpool_send_timeouts)
// The tunable value needs to be a string literal or have the lifetime
// managed by the caller - I could strdup locally, but don't want to be
// flagged by valgrind.
#define MAKE_CDB2API_TEST_TUNABLE(name) \
static const char *cdb2api_test_##name = ""; \
void set_cdb2api_test_##name(const char *value) \
{ \
cdb2api_test_##name = value; \
}
MAKE_CDB2API_TEST_TUNABLE(comdb2db_cfg)
MAKE_CDB2API_TEST_TUNABLE(dbname_cfg)
int get_dbinfo_state(void)
{
return get_dbinfo;
}
void set_dbinfo_state(int value)
{
get_dbinfo = value;
}
void set_allow_pmux_route(int allow)
{
cdb2_allow_pmux_route = allow;
}
void set_local_connections_limit(int lim)
{
max_local_connection_cache_entries = lim;
}
static int sent_valid_identity = 0;
static char sent_identity_principal[500] = {0};
int sent_iam_identity(void)
{
return sent_valid_identity;
}
char *sent_id_principal()
{
return sent_identity_principal;
}
const char *get_default_cluster(void)
{
return cdb2_default_cluster;
}
const char *get_default_cluster_hndl(cdb2_hndl_tp *hndl)
{
return hndl->type;
}
void set_protobuf_size(int size)
{
CDB2_PROTOBUF_SIZE = size;
}
int get_gbl_event_version(void)
{
return cdb2_gbl_event_version;
}
#endif /* CDB2API_TEST */
#define PROCESS_EVENT_CTRL_BEFORE(h, e, rc, callbackrc, ovwrrc) \
do { \
if (e->ctrls & CDB2_OVERWRITE_RETURN_VALUE) { \
ovwrrc = 1; \
rc = (int)(intptr_t)callbackrc; \
} \
if (e->ctrls & CDB2_AS_HANDLE_SPECIFIC_ARG) \
h->user_arg = callbackrc; \
} while (0)
#define PROCESS_EVENT_CTRL_AFTER(h, e, rc, callbackrc) \
do { \
if (e->ctrls & CDB2_OVERWRITE_RETURN_VALUE) { \
rc = (int)(intptr_t)callbackrc; \
} \
if (e->ctrls & CDB2_AS_HANDLE_SPECIFIC_ARG) \
h->user_arg = callbackrc; \
} while (0)
typedef void (*cdb2_init_t)(void);
/* Undocumented compile-time library installation/uninstallation routine. */
#ifndef CDB2_INSTALL_LIBS
#define CDB2_INSTALL_LIBS NULL
#else
extern void CDB2_INSTALL_LIBS(const char *);
#endif
void (*cdb2_install)(const char *) = CDB2_INSTALL_LIBS;
#ifndef CDB2_UNINSTALL_LIBS
#define CDB2_UNINSTALL_LIBS NULL
#else
extern void CDB2_UNINSTALL_LIBS(const char *);
#endif
void (*cdb2_uninstall)(const char *) = CDB2_UNINSTALL_LIBS;
#ifndef CDB2_IDENTITY_CALLBACKS
struct cdb2_identity *identity_cb = NULL;
#else
extern struct cdb2_identity CDB2_IDENTITY_CALLBACKS;
struct cdb2_identity *identity_cb = &CDB2_IDENTITY_CALLBACKS;
#endif
#ifndef CDB2_PUBLISH_EVENT_CALLBACKS
struct cdb2_publish_event *publish_event_cb = NULL;
#else
extern struct cdb2_publish_event CDB2_PUBLISH_EVENT_CALLBACKS;
struct cdb2_publish_event *publish_event_cb = &CDB2_PUBLISH_EVENT_CALLBACKS;
#endif
pthread_mutex_t cdb2_sockpool_mutex = PTHREAD_MUTEX_INITIALIZER;
#define MAX_SOCKPOOL_FDS 8
#include <netdb.h>
static char *cdb2_type_str(int type)
{
switch (type) {
case CDB2_INTEGER:
return "CDB2_INTEGER";
case CDB2_REAL:
return "CDB2_REAL";
case CDB2_CSTRING:
return "CDB2_CSTRING";
case CDB2_BLOB:
return "CDB2_BLOB";
case CDB2_DATETIME:
return "CDB2_DATETIME";
case CDB2_INTERVALYM:
return "CDB2_INTERVALYM";
case CDB2_INTERVALDS:
return "CDB2_INTERVALDS";
case CDB2_DATETIMEUS:
return "CDB2_DATETIMEUS";
case CDB2_INTERVALDSUS:
return "CDB2_INTERVALDSUS";
default:
return "???";
}
}
#define debugprint(fmt, args...) \
do { \
if (hndl && hndl->debug_trace) \
fprintf(stderr, "td 0x%p %s:%d " fmt, (void *)pthread_self(), \
__func__, __LINE__, ##args); \
} while (0);
COMDB2BUF *cdb2_cdb2buf_openread(const char *filename)
{
int fd;
COMDB2BUF *s;
if ((fd = open(filename, O_RDONLY, 0)) < 0 ||
(s = cdb2buf_open(fd, 0)) == NULL) {
if (fd >= 0)
close(fd);
return NULL;
}
return s;
}
#if defined(__APPLE__)
#include <libproc.h>
static char *apple_getargv0(void)
{
static char argv0[PATH_MAX];
int ret = proc_pidpath(_PID, argv0, sizeof(argv0));
if (ret <= 0) {
fprintf(stderr, "%s proc_pidpath returns %d\n", __func__, ret);
return NULL;
}
return argv0;
}
#endif
#if defined(_SUN_SOURCE) || defined(_LINUX_SOURCE)
static char *proc_cmdline_getargv0(void)
{
char procname[64];
static char argv0[PATH_MAX];
snprintf(procname, sizeof(procname), "/proc/self/cmdline");
COMDB2BUF *s = cdb2_cdb2buf_openread(procname);
if (s == NULL) {
fprintf(stderr, "%s cannot open %s, %s\n", __func__, procname,
strerror(errno));
return NULL;
}
if ((cdb2buf_gets(argv0, PATH_MAX, s)) < 0) {
fprintf(stderr, "%s error reading from %s, %s\n", __func__, procname,
strerror(errno));
cdb2buf_close(s);
return NULL;
}
cdb2buf_close(s);
return argv0;
}
#endif
char *cdb2_getargv0(void)
{
#if defined(__APPLE__)
return apple_getargv0();
#elif defined(_LINUX_SOURCE) || defined(_SUN_SOURCE)
return proc_cmdline_getargv0();
#else
fprintf(stderr, "%s unsupported architecture\n", __func__);
return NULL;
#endif
}
static void sockpool_close_all(void);
#ifndef CDB2API_TEST
static
#endif
void
local_connection_cache_clear(int);
static void atfork_prepare(void) {
pthread_mutex_lock(&cdb2_sockpool_mutex);
if (identity_cb)
identity_cb->resetIdentity_start();
}
static void atfork_me(void) {
if (identity_cb)
identity_cb->resetIdentity_end(1);
pthread_mutex_unlock(&cdb2_sockpool_mutex);
}
static void atfork_child(void) {
local_connection_cache_clear(0);
sockpool_close_all();
local_connection_cache_owner_pid = _PID = getpid();
if (identity_cb)
identity_cb->resetIdentity_end(0);
pthread_mutex_unlock(&cdb2_sockpool_mutex);
}
// Prints a warning if the env var's value is invalid.
//
// Invalid values are those that aren't integers ("abc", "abc123", "123abc", ...)
static int process_env_var_int(const char *var, int *value, int *indicator)
{
char *s = getenv(var);
if (s) {
char *endp = NULL;
int val = strtol(s, &endp, 10);
if (endp && *endp == 0) {
*value = val;
if (endp == s) {
fprintf(stderr, "WARNING: %s: Value of %s is not valid. Using value: %d.\n", __func__, var, *value);
}
if (indicator)
*indicator = 1;
return 0;
} else {
fprintf(stderr, "WARNING: %s: Value of %s is not valid. Using value: %d.\n", __func__, var, *value);
}
}
return 1;
}
// Prints a warning if the env var's value is invalid.
//
// An env var's value is invalid if it is equal to the empty string or is longer than `len`.
static int process_env_var_str(const char *var, char *value, int len, int *indicator)
{
char *s = getenv(var);
if (s) {
if (((strlen(s) + 1) <= len)) {
strncpy(value, s, len);
if (strcmp(value, "") == 0) {
fprintf(stderr, "WARNING: %s: Value of %s is not valid. Using value '%s'.\n", __func__, var, value);
}
if (indicator)
*indicator = 1;
return 0;
} else {
fprintf(stderr, "WARNING: %s: Value of %s is not valid. Using value '%s'.\n", __func__, var, value);
}
}
return 1;
}
static int value_on_off(const char *value, int *err);
// Prints a warning if the env var's value is invalid.
//
// See `value_on_off` for invalid values.
static int process_env_var_str_on_off(const char *var, int *value, int *indicator)
{
char *s = getenv(var);
int err;
if (s) {
*value = value_on_off(s, &err);
if (err == -1) {
fprintf(stderr, "WARNING: %s: Value of %s is not valid. Using value '%s'.\n", __func__, var,
*value == 0 ? "OFF" : "ON");
}
if (indicator)
*indicator = 1;
return 0;
}
return 1;
}
#ifdef CDB2API_TEST
char cdb2dbconfig_singleconfig[511];
int cdb2cfg_override_all_config_paths = 0;
void set_cdb2api_test_single_cfg(const char *cfg_file)
{
cdb2cfg_override_all_config_paths = 1;
strncpy(cdb2dbconfig_singleconfig, cfg_file, 511);
}
#endif
static inline int get_char(COMDB2BUF *s, const char *buf, int *chrno)
{
int ch;
if (s) {
ch = cdb2buf_getc(s);
} else {
ch = buf[*chrno];
*chrno += 1;
}
return ch;
}
int cdb2_read_line(char *line, int maxlen, COMDB2BUF *s, const char *buf, int *chrno)
{
int ch = get_char(s, buf, chrno);
while (ch == ' ' || ch == '\n')
ch = get_char(s, buf, chrno); // consume empty lines
int count = 0;
while ((ch != '\n') && (ch != EOF) && (ch != '\0')) {
line[count] = ch;
count++;
if (count >= maxlen)
return count;
ch = get_char(s, buf, chrno);
}
if (count == 0)
return -1;
line[count + 1] = '\0';
return count + 1;
}
static void process_env_vars(void)
{
process_env_var_int("COMDB2_CONFIG_MAX_LOCAL_CONNECTION_CACHE_ENTRIES", &max_local_connection_cache_entries,
&max_local_connection_cache_entries_envvar);
process_env_var_int("COMDB2_CONFIG_MAX_LOCAL_CONNECTION_CACHE_AGE", &max_local_connection_cache_age,
&max_local_connection_cache_age_envvar);
process_env_var_int("COMDB2_CONFIG_LOCAL_CONNECTION_CACHE_USE_SBUF", &local_connection_cache_use_sbuf,
&local_connection_cache_use_sbuf_envvar);
process_env_var_int("COMDB2_CONFIG_LOCAL_CONNECTION_CACHE_CHECK_PID", &local_connection_cache_check_pid,
&local_connection_cache_check_pid_envvar);
process_env_var_str_on_off("COMDB2_CONFIG_USE_ENV_VARS", &cdb2_use_env_vars, 0);
if (cdb2_use_env_vars) {
char *default_type = getenv("COMDB2_CONFIG_DEFAULT_TYPE");
if (default_type) {
default_type_override_env = 1;
}
}
char *do_log = getenv("CDB2_LOG_CALLS");
if (do_log)
log_calls = 1;
#ifdef CDB2API_TEST
char *config = getenv("CDB2_CONFIG_FILE");
if (config) {
/* can't call back cdb2_set_comdb2db_config from do_init_once */
strncpy(CDB2DBCONFIG_NOBBENV, config, 511);
}
char *cdb2db_override = getenv("CDB2_COMDB2DB_OVERRIDE");
if (cdb2db_override) {
strncpy(COMDB2DB_OVERRIDE, cdb2db_override, 31);
}
char *cdb2db_dbnum_override = getenv("CDB2_COMDB2DB_DBNUM_OVERRIDE");
if (cdb2db_dbnum_override) {
COMDB2DB_NUM_OVERRIDE = atoi(cdb2db_dbnum_override);
}
char *min_retries = getenv("COMDB2_CONFIG_MIN_RETRIES");
if (min_retries) {
MIN_RETRIES = atoi(min_retries);
}
#endif
}
#ifdef CDB2API_TEST
void test_process_env_vars(void)
{
process_env_vars();
}
int get_max_local_connection_cache_entries(void)
{
return max_local_connection_cache_entries;
}
#endif
static int init_once_has_run = 0;
#ifdef CDB2API_TEST
void reset_once(void)
{
init_once_has_run = 0;
}
#endif
static void do_init_once(void)
{
if (!init_once_has_run) {
srandom(time(0));
local_connection_cache_owner_pid = _PID = getpid();
_MACHINE_ID = gethostid();
_ARGV0 = cdb2_getargv0();
pthread_atfork(atfork_prepare, atfork_me, atfork_child);
TAILQ_INIT(&local_connection_cache);
TAILQ_INIT(&free_local_connection_cache);
process_env_vars();
init_once_has_run = 1;
}
}
#define cdb2_skipws(str) \
do { \
while (*(str) && isspace(*(str))) \
++(str); \
} while (0);
/* if sqlstr is a read stmt will return 1 otherwise return 0
* returns -1 if sqlstr is null
*/
static int is_sql_read(const char *sqlstr)
{
const char get[] = "GET";
const char sp_exec[] = "EXEC";
const char with[] = "WITH";
const char sel[] = "SELECT";
const char explain[] = "EXPLAIN";
if (sqlstr == NULL)
return -1;
cdb2_skipws(sqlstr);
int slen = strlen(sqlstr);
if (slen) {
if (slen < sizeof(get) - 1)
return 0;
if (!strncasecmp(sqlstr, get, sizeof(get) - 1))
return 1;
if (slen < sizeof(sp_exec) - 1)
return 0;
if (!strncasecmp(sqlstr, sp_exec, sizeof(sp_exec) - 1))
return 1;
if (!strncasecmp(sqlstr, with, sizeof(with) - 1))
return 1;
if (slen < sizeof(sel) - 1)
return 0;
if (!strncasecmp(sqlstr, sel, sizeof(sel) - 1))
return 1;
if (slen < sizeof(explain) - 1)
return 0;
if (!strncasecmp(sqlstr, explain, sizeof(explain) - 1))
return 1;
}
return 0;
}
/* PASSFD CODE */
#if defined(_LINUX_SOURCE)
#define HAVE_MSGHDR_MSG_CONTROL
#endif
enum {
PASSFD_SUCCESS = 0,
PASSFD_RECVMSG = -1, /* error with recvmsg() */
PASSFD_EOF = -2, /* eof before message completely read */
PASSFD_2FDS = -3, /* received more than one file descriptor */
PASSFD_BADCTRL = -4, /* received bad control message */
PASSFD_TIMEOUT = -5, /* timed out */
PASSFD_POLL = -6, /* error with poll() */
PASSFD_SENDMSG = -7 /* error with sendmsg() */
};
static int recv_fd_int(int sockfd, void *data, size_t nbytes, int *fd_recvd, int timeoutms)
{
ssize_t rc;
size_t bytesleft;
char *cdata;
struct msghdr msg;
struct iovec iov[1];
int recvfd;
#ifdef HAVE_MSGHDR_MSG_CONTROL
union {
struct cmsghdr cm;
unsigned char control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr *cmsgptr;
#endif
*fd_recvd = -1;
cdata = data;
bytesleft = nbytes;
while (bytesleft > 0) {
if (timeoutms > 0) {
struct pollfd pol;
int pollrc;
pol.fd = sockfd;
pol.events = POLLIN;
pollrc = poll(&pol, 1, timeoutms);
#ifdef CDB2API_TEST
if (fail_timeout_sockpool_recv) {
fail_timeout_sockpool_recv--;
num_sockpool_recv_timeouts++;
return PASSFD_TIMEOUT;
}
#endif
if (pollrc == 0) {
#ifdef CDB2API_TEST
num_sockpool_recv_timeouts++;
#endif
return PASSFD_TIMEOUT;
} else if (pollrc == -1) {
/* error will be in errno */
return PASSFD_POLL;
}
}
#ifdef HAVE_MSGHDR_MSG_CONTROL
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
msg.msg_flags = 0;
#else
msg.msg_accrights = (caddr_t)&recvfd;
msg.msg_accrightslen = sizeof(int);
#endif
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
iov[0].iov_base = cdata;
iov[0].iov_len = bytesleft;
rc = recvmsg(sockfd, &msg, 0);
if (rc == -1) {
if (errno == EINTR || errno == EAGAIN)
continue;
return PASSFD_RECVMSG;
}
if (rc == 0) {
/* Premature eof */
return PASSFD_EOF;
}
cdata += rc;
bytesleft -= rc;
/* See if we got a descriptor with this message */
#ifdef HAVE_MSGHDR_MSG_CONTROL
cmsgptr = CMSG_FIRSTHDR(&msg);
if (cmsgptr) {
if (cmsgptr->cmsg_len != CMSG_LEN(sizeof(int)) ||
cmsgptr->cmsg_level != SOL_SOCKET ||
cmsgptr->cmsg_type != SCM_RIGHTS) {
return PASSFD_BADCTRL;
}
recvfd = *((int *)CMSG_DATA(cmsgptr));
if (*fd_recvd != -1) {
if (close(recvfd) == -1) {
fprintf(stderr, "%s: error closing second fd %d: %d %s\n",
__func__, recvfd, errno, strerror(errno));
}
return PASSFD_2FDS;
}
*fd_recvd = recvfd;
if (CMSG_NXTHDR(&msg, cmsgptr)) {
return PASSFD_BADCTRL;
}
}
#else
if (msg.msg_accrightslen == sizeof(int)) {
if (*fd_recvd != -1) {
if (close(recvfd) == -1) {
fprintf(stderr, "%s: error closing second fd %d: %d %s\n",
__func__, recvfd, errno, strerror(errno));
}
return PASSFD_2FDS;
}
*fd_recvd = recvfd;