Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : /*
14 : * author Martin Kersten, Fabian Groffen
15 : * Client Management
16 : * Each online client is represented with an entry in the clients table.
17 : * The client may inspect his record at run-time and partially change its properties.
18 : * The administrator sees all client records and has the right to adjust global properties.
19 : */
20 :
21 :
22 : #include "monetdb_config.h"
23 : #include "clients.h"
24 : #include "mcrypt.h"
25 : #include "mal_scenario.h"
26 : #include "mal_instruction.h"
27 : #include "mal_runtime.h"
28 : #include "mal_client.h"
29 : #include "mal_authorize.h"
30 : #include "mal_internal.h"
31 : #include "opt_pipes.h"
32 : #include "gdk_time.h"
33 :
34 : static str
35 1 : CLTsetListing(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
36 : {
37 1 : (void) mb;
38 1 : *getArgReference_int(stk, pci, 0) = cntxt->listing;
39 1 : cntxt->listing = *getArgReference_int(stk, pci, 1);
40 1 : return MAL_SUCCEED;
41 : }
42 :
43 : static str
44 1 : CLTgetClientId(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
45 : {
46 1 : (void) mb;
47 1 : if (cntxt - mal_clients < 0 || cntxt - mal_clients >= MAL_MAXCLIENTS)
48 0 : throw(MAL, "clients.getClientId", "Illegal client index");
49 1 : *getArgReference_int(stk, pci, 0) = (int) (cntxt - mal_clients);
50 1 : return MAL_SUCCEED;
51 : }
52 :
53 : static str
54 0 : CLTgetScenario(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
55 : {
56 0 : (void) mb;
57 0 : if (cntxt->scenario)
58 0 : *getArgReference_str(stk, pci, 0) = GDKstrdup(cntxt->scenario);
59 : else
60 0 : *getArgReference_str(stk, pci, 0) = GDKstrdup("nil");
61 0 : if (*getArgReference_str(stk, pci, 0) == NULL)
62 0 : throw(MAL, "clients.getScenario", SQLSTATE(HY013) MAL_MALLOC_FAIL);
63 : return MAL_SUCCEED;
64 : }
65 :
66 : static str
67 0 : CLTsetScenario(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
68 : {
69 0 : str msg = MAL_SUCCEED;
70 :
71 0 : (void) mb;
72 0 : msg = setScenario(cntxt, *getArgReference_str(stk, pci, 1));
73 0 : *getArgReference_str(stk, pci, 0) = 0;
74 0 : if (msg == NULL) {
75 0 : *getArgReference_str(stk, pci, 0) = GDKstrdup(cntxt->scenario);
76 0 : if (*getArgReference_str(stk, pci, 0) == NULL)
77 0 : throw(MAL, "clients.setScenario", SQLSTATE(HY013) MAL_MALLOC_FAIL);
78 : }
79 : return msg;
80 : }
81 :
82 : static void
83 1 : CLTtimeConvert(time_t l, char *s)
84 : {
85 1 : struct tm localt = (struct tm) { 0 };
86 :
87 1 : (void) localtime_r(&l, &localt);
88 :
89 : #ifdef HAVE_ASCTIME_R3
90 : asctime_r(&localt, s, 26);
91 : #else
92 1 : asctime_r(&localt, s);
93 : #endif
94 1 : s[24] = 0; /* remove newline */
95 1 : }
96 :
97 : static str
98 1 : CLTInfo(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
99 : {
100 1 : bat *ret = getArgReference_bat(stk, pci, 0);
101 1 : bat *ret2 = getArgReference_bat(stk, pci, 1);
102 1 : BAT *b = COLnew(0, TYPE_str, 12, TRANSIENT);
103 1 : BAT *bn = COLnew(0, TYPE_str, 12, TRANSIENT);
104 1 : char buf[32]; /* 32 bytes are enough */
105 :
106 1 : (void) mb;
107 1 : if (b == 0 || bn == 0) {
108 0 : BBPreclaim(b);
109 0 : BBPreclaim(bn);
110 0 : throw(MAL, "clients.info", SQLSTATE(HY013) MAL_MALLOC_FAIL);
111 : }
112 :
113 1 : (void) snprintf(buf, sizeof(buf), "" LLFMT "", (lng) cntxt->user);
114 2 : if (BUNappend(b, "user", false) != GDK_SUCCEED ||
115 1 : BUNappend(bn, buf, false) != GDK_SUCCEED)
116 0 : goto bailout;
117 :
118 2 : if (BUNappend(b, "scenario", false) != GDK_SUCCEED ||
119 1 : BUNappend(bn, cntxt->scenario, false) != GDK_SUCCEED)
120 0 : goto bailout;
121 :
122 1 : (void) snprintf(buf, sizeof(buf), "%d", cntxt->listing);
123 2 : if (BUNappend(b, "listing", false) != GDK_SUCCEED ||
124 1 : BUNappend(bn, buf, false) != GDK_SUCCEED)
125 0 : goto bailout;
126 :
127 1 : CLTtimeConvert(cntxt->login, buf);
128 2 : if (BUNappend(b, "login", false) != GDK_SUCCEED ||
129 1 : BUNappend(bn, buf, false) != GDK_SUCCEED)
130 0 : goto bailout;
131 1 : *ret = b->batCacheid;
132 1 : BBPkeepref(b);
133 1 : *ret2 = bn->batCacheid;
134 1 : BBPkeepref(bn);
135 1 : return MAL_SUCCEED;
136 :
137 0 : bailout:
138 0 : BBPunfix(b->batCacheid);
139 0 : BBPunfix(bn->batCacheid);
140 0 : throw(MAL, "clients.info", SQLSTATE(HY013) MAL_MALLOC_FAIL);
141 : }
142 :
143 : static str
144 0 : CLTLogin(bat *nme, bat *ret)
145 : {
146 0 : BAT *b = COLnew(0, TYPE_str, 12, TRANSIENT);
147 0 : BAT *u = COLnew(0, TYPE_oid, 12, TRANSIENT);
148 0 : int i;
149 0 : char s[32];
150 :
151 0 : if (b == 0 || u == 0)
152 0 : goto bailout;
153 :
154 0 : for (i = 0; i < MAL_MAXCLIENTS; i++) {
155 0 : Client c = mal_clients + i;
156 0 : if (c->mode >= RUNCLIENT && !is_oid_nil(c->user)) {
157 0 : CLTtimeConvert(c->login, s);
158 0 : if (BUNappend(b, s, false) != GDK_SUCCEED ||
159 0 : BUNappend(u, &c->user, false) != GDK_SUCCEED)
160 0 : goto bailout;
161 : }
162 : }
163 0 : *ret = b->batCacheid;
164 0 : BBPkeepref(b);
165 0 : *nme = u->batCacheid;
166 0 : BBPkeepref(u);
167 0 : return MAL_SUCCEED;
168 :
169 0 : bailout:
170 0 : BBPreclaim(b);
171 0 : BBPreclaim(u);
172 0 : throw(MAL, "clients.getLogins", SQLSTATE(HY013) MAL_MALLOC_FAIL);
173 : }
174 :
175 : static str
176 1 : CLTquit(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
177 : {
178 1 : str msg = MAL_SUCCEED;
179 1 : int idx = cntxt->idx;
180 1 : (void) mb; /* fool compiler */
181 :
182 1 : if (pci->argc == 2) {
183 0 : if (cntxt->user == MAL_ADMIN)
184 0 : idx = *getArgReference_int(stk, pci, 1);
185 : else
186 0 : throw(MAL, "clients.quit",
187 : SQLSTATE(42000) "Administrator rights required");
188 : }
189 :
190 1 : if (idx < 0 || idx > MAL_MAXCLIENTS)
191 0 : throw(MAL, "clients.quit", "Illegal session id");
192 :
193 : /* A user can only quit a session under the same id */
194 1 : MT_lock_set(&mal_contextLock);
195 1 : if (mal_clients[idx].mode == FREECLIENT)
196 0 : msg = createException(MAL, "clients.stop",
197 : "Session not active anymore");
198 : else
199 1 : mal_clients[idx].mode = FINISHCLIENT;
200 1 : MT_lock_unset(&mal_contextLock);
201 1 : return msg;
202 : }
203 :
204 : /* Stopping a client in a soft manner by setting the time out marker */
205 : static str
206 0 : CLTstop(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
207 : {
208 0 : int idx = cntxt->idx;
209 0 : str msg = MAL_SUCCEED;
210 :
211 0 : (void) mb;
212 0 : if (cntxt->user != MAL_ADMIN)
213 0 : throw(MAL, "clients.stop",
214 : SQLSTATE(42000) "Administrator rights required");
215 :
216 0 : idx = *getArgReference_int(stk, pci, 1);
217 :
218 0 : if (idx < 0 || idx > MAL_MAXCLIENTS)
219 0 : throw(MAL, "clients.stop", "Illegal session id");
220 :
221 0 : MT_lock_set(&mal_contextLock);
222 0 : if (mal_clients[idx].mode == FREECLIENT)
223 0 : msg = createException(MAL, "clients.stop",
224 : "Session not active anymore");
225 : else
226 0 : mal_clients[idx].qryctx.querytimeout = 1; /* stop client in one microsecond */
227 : /* this forces the designated client to stop at the next instruction */
228 0 : MT_lock_unset(&mal_contextLock);
229 0 : return msg;
230 : }
231 :
232 : static str
233 4 : CLTsetoptimizer(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
234 : {
235 4 : int idx = cntxt->idx;
236 4 : str opt, msg = MAL_SUCCEED;
237 :
238 4 : (void) mb;
239 4 : if (pci->argc == 3) {
240 0 : if (cntxt->user == MAL_ADMIN) {
241 0 : idx = *getArgReference_int(stk, pci, 1);
242 0 : opt = *getArgReference_str(stk, pci, 2);
243 : } else {
244 0 : throw(MAL, "clients.setoptimizer",
245 : SQLSTATE(42000) "Administrator rights required");
246 : }
247 : } else {
248 4 : opt = *getArgReference_str(stk, pci, 1);
249 : }
250 :
251 4 : if (idx < 0 || idx > MAL_MAXCLIENTS)
252 0 : throw(MAL, "clients.setoptimizer", "Illegal session id");
253 4 : if (strNil(opt))
254 0 : throw(MAL, "clients.setoptimizer", "Input string cannot be NULL");
255 4 : if (strlen(opt) >= sizeof(mal_clients[idx].optimizer))
256 0 : throw(MAL, "clients.setoptimizer", "Input string is too large");
257 4 : if (!isOptimizerPipe(opt))
258 1 : throw(MAL, "clients.setoptimizer", "Valid optimizer pipe expected");
259 :
260 3 : MT_lock_set(&mal_contextLock);
261 3 : if (mal_clients[idx].mode == FREECLIENT)
262 0 : msg = createException(MAL, "clients.setoptimizer",
263 : "Session not active anymore");
264 : else
265 3 : strcpy_len(mal_clients[idx].optimizer, opt,
266 : sizeof(mal_clients[idx].optimizer));
267 3 : MT_lock_unset(&mal_contextLock);
268 3 : return msg;
269 : }
270 :
271 : static str
272 4 : CLTsetworkerlimit(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
273 : {
274 4 : str msg = MAL_SUCCEED;
275 4 : int idx = cntxt->idx, limit;
276 :
277 4 : (void) mb;
278 4 : if (pci->argc == 3) {
279 0 : if (cntxt->user == MAL_ADMIN) {
280 0 : idx = *getArgReference_int(stk, pci, 1);
281 0 : limit = *getArgReference_int(stk, pci, 2);
282 : } else {
283 0 : throw(MAL, "clients.setworkerlimit",
284 : SQLSTATE(42000) "Administrator rights required");
285 : }
286 : } else {
287 4 : limit = *getArgReference_int(stk, pci, 1);
288 : }
289 :
290 4 : if (idx < 0 || idx > MAL_MAXCLIENTS)
291 0 : throw(MAL, "clients.setworkerlimit", "Illegal session id");
292 4 : if (is_int_nil(limit))
293 0 : throw(MAL, "clients.setworkerlimit",
294 : "The number of workers cannot be NULL");
295 4 : if (limit < 0)
296 1 : throw(MAL, "clients.setworkerlimit",
297 : "The number of workers cannot be negative");
298 :
299 3 : MT_lock_set(&mal_contextLock);
300 3 : if (mal_clients[idx].mode == FREECLIENT)
301 0 : msg = createException(MAL, "clients.setworkerlimit",
302 : "Session not active anymore");
303 : else {
304 3 : if (limit == 0) {
305 0 : if (mal_clients[idx].maxworkers > 0)
306 : limit = mal_clients[idx].maxworkers;
307 3 : } else if (cntxt->user != MAL_ADMIN &&
308 0 : mal_clients[idx].maxworkers > 0 &&
309 : mal_clients[idx].maxworkers < limit) {
310 3 : limit = mal_clients[idx].maxworkers;
311 : }
312 3 : mal_clients[idx].workerlimit = limit;
313 : }
314 3 : MT_lock_unset(&mal_contextLock);
315 3 : return msg;
316 : }
317 :
318 : static str
319 0 : CLTsetmemorylimit(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
320 : {
321 0 : str msg = MAL_SUCCEED;
322 0 : int idx = cntxt->idx, limit;
323 :
324 0 : (void) mb;
325 0 : if (pci->argc == 3) {
326 0 : if (cntxt->user == MAL_ADMIN) {
327 0 : idx = *getArgReference_sht(stk, pci, 1);
328 0 : limit = *getArgReference_int(stk, pci, 2);
329 : } else {
330 0 : throw(MAL, "clients.setmemorylimit",
331 : SQLSTATE(42000) "Administrator rights required");
332 : }
333 : } else {
334 0 : limit = *getArgReference_int(stk, pci, 1);
335 : }
336 :
337 0 : if (idx < 0 || idx > MAL_MAXCLIENTS)
338 0 : throw(MAL, "clients.setmemorylimit", "Illegal session id");
339 0 : if (is_int_nil(limit))
340 0 : throw(MAL, "clients.setmemorylimit",
341 : "The memmory limit cannot be NULL");
342 0 : if (limit < 0)
343 0 : throw(MAL, "clients.setmemorylimit",
344 : "The memmory limit cannot be negative");
345 :
346 0 : lng mlimit = (lng) limit << 20;
347 :
348 0 : MT_lock_set(&mal_contextLock);
349 0 : if (mal_clients[idx].mode == FREECLIENT)
350 0 : msg = createException(MAL, "clients.setmemorylimit",
351 : "Session not active anymore");
352 : else {
353 0 : if (mlimit == 0) {
354 0 : if (mal_clients[idx].maxmem > 0)
355 : mlimit = mal_clients[idx].maxmem;
356 0 : } else if (cntxt->user != MAL_ADMIN &&
357 0 : mal_clients[idx].maxmem > 0 &&
358 : mal_clients[idx].maxmem < mlimit) {
359 0 : mlimit = mal_clients[idx].maxmem;
360 : }
361 0 : mal_clients[idx].memorylimit = (int) (mlimit >> 20);
362 0 : mal_clients[idx].qryctx.maxmem = (ATOMIC_BASE_TYPE) mlimit;
363 : }
364 0 : MT_lock_unset(&mal_contextLock);
365 0 : return msg;
366 : }
367 :
368 : static str
369 0 : CLTstopSession(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
370 : {
371 0 : str msg = MAL_SUCCEED;
372 0 : int idx = cntxt->idx;
373 0 : (void) mb;
374 :
375 0 : if (cntxt->user != MAL_ADMIN) {
376 0 : throw(MAL, "clients.stopsession",
377 : SQLSTATE(42000) "Administrator rights required");
378 : }
379 0 : idx = *getArgReference_int(stk, pci, 1);
380 0 : if (idx < 0 || idx > MAL_MAXCLIENTS)
381 0 : throw(MAL, "clients.stopSession", "Illegal session id");
382 :
383 0 : MT_lock_set(&mal_contextLock);
384 0 : if (mal_clients[idx].mode == FREECLIENT) {
385 0 : msg = createException(MAL, "clients.stopSession",
386 : "Session not active anymore");
387 : } else {
388 0 : mal_clients[idx].qryctx.querytimeout = 1; /* stop client in one microsecond */
389 0 : mal_clients[idx].sessiontimeout = 1; /* stop client session */
390 : }
391 0 : MT_lock_unset(&mal_contextLock);
392 : /* this forces the designated client to stop at the next instruction */
393 0 : return msg;
394 : }
395 :
396 : /* Queries can be temporarily suspended */
397 : static str
398 0 : CLTsuspend(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
399 : {
400 0 : str msg = MAL_SUCCEED;
401 0 : int idx = cntxt->idx;
402 :
403 0 : if (cntxt->user != MAL_ADMIN)
404 0 : throw(MAL, "clients.suspend",
405 : SQLSTATE(42000) "Administrator rights required");
406 :
407 0 : idx = *getArgReference_int(stk, pci, 1);
408 0 : (void) mb;
409 :
410 0 : if (idx < 0 || idx > MAL_MAXCLIENTS)
411 0 : throw(MAL, "clients.suspend", "Illegal session id");
412 :
413 0 : MT_lock_set(&mal_contextLock);
414 0 : if (mal_clients[idx].mode == FREECLIENT)
415 0 : msg = createException(MAL, "clients.suspend",
416 : "Session not active anymore");
417 : else
418 0 : msg = MCsuspendClient(idx);
419 0 : MT_lock_unset(&mal_contextLock);
420 0 : return msg;
421 : }
422 :
423 : static str
424 0 : CLTwakeup(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
425 : {
426 0 : str msg = MAL_SUCCEED;
427 0 : int idx = cntxt->idx;
428 :
429 0 : if (cntxt->user != MAL_ADMIN)
430 0 : throw(MAL, "clients.wakeup",
431 : SQLSTATE(42000) "Administrator rights required");
432 :
433 0 : idx = *getArgReference_int(stk, pci, 1);
434 0 : (void) mb;
435 :
436 0 : if (idx < 0 || idx > MAL_MAXCLIENTS)
437 0 : throw(MAL, "clients.wakeup", "Illegal session id");
438 :
439 0 : MT_lock_set(&mal_contextLock);
440 0 : if (mal_clients[idx].mode == FREECLIENT)
441 0 : msg = createException(MAL, "clients.wakeup",
442 : "Session not active anymore");
443 : else
444 0 : msg = MCawakeClient(idx);
445 0 : MT_lock_unset(&mal_contextLock);
446 0 : return msg;
447 : }
448 :
449 : /* set query timeout based in seconds, converted into microseconds */
450 : static str
451 19 : CLTqueryTimeout(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
452 : {
453 19 : str msg = MAL_SUCCEED;
454 19 : int qto, idx = cntxt->idx;
455 19 : (void) mb;
456 :
457 19 : if (pci->argc == 3) {
458 0 : if (cntxt->user == MAL_ADMIN) {
459 0 : idx = *getArgReference_int(stk, pci, 1);
460 0 : qto = *getArgReference_int(stk, pci, 2);
461 : } else {
462 0 : throw(MAL, "clients.setquerytimeout",
463 : SQLSTATE(42000) "Administrator rights required");
464 : }
465 : } else {
466 19 : qto = *getArgReference_int(stk, pci, 1);
467 : }
468 19 : if (is_int_nil(qto))
469 0 : throw(MAL, "clients.setquerytimeout", "Query timeout cannot be NULL");
470 19 : if (qto < 0)
471 1 : throw(MAL, "clients.setquerytimeout", "Query timeout should be >= 0");
472 :
473 18 : MT_lock_set(&mal_contextLock);
474 18 : if (mal_clients[idx].mode == FREECLIENT)
475 0 : msg = createException(MAL, "clients.setquerytimeout",
476 : "Session not active anymore");
477 : else {
478 : /* when testing (FORCEMITOMASK), reduce timeout of 1 sec to 1 msec */
479 36 : lng timeout_micro = ATOMIC_GET(&GDKdebug) & FORCEMITOMASK
480 18 : && qto == 1 ? 1000 : (lng) qto * 1000000;
481 18 : mal_clients[idx].qryctx.querytimeout = timeout_micro;
482 18 : QryCtx *qry_ctx = MT_thread_get_qry_ctx();
483 18 : if (qry_ctx)
484 18 : qry_ctx->querytimeout = timeout_micro;
485 : }
486 18 : MT_lock_unset(&mal_contextLock);
487 18 : return msg;
488 : }
489 :
490 : // set query timeout based in microseconds
491 : static str
492 2 : CLTqueryTimeoutMicro(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
493 : {
494 2 : str msg = MAL_SUCCEED;
495 2 : int idx = cntxt->idx;
496 2 : lng qto = *getArgReference_lng(stk, pci, 1);
497 2 : (void) mb;
498 :
499 2 : if (is_lng_nil(qto))
500 0 : throw(MAL, "clients.queryTimeout", "Query timeout cannot be NULL");
501 2 : if (qto < 0)
502 0 : throw(MAL, "clients.queryTimeout", "Query timeout should be >= 0");
503 :
504 2 : MT_lock_set(&mal_contextLock);
505 2 : if (mal_clients[idx].mode == FREECLIENT)
506 0 : msg = createException(MAL, "clients.queryTimeout",
507 : "Session not active anymore");
508 : else {
509 2 : mal_clients[idx].qryctx.querytimeout = qto;
510 2 : QryCtx *qry_ctx = MT_thread_get_qry_ctx();
511 2 : if (qry_ctx)
512 2 : qry_ctx->querytimeout = qto;
513 : }
514 2 : MT_lock_unset(&mal_contextLock);
515 2 : return msg;
516 : }
517 :
518 : /* Set the current session timeout in seconds */
519 : static str
520 2690 : CLTsessionTimeout(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
521 : {
522 2690 : str msg = MAL_SUCCEED;
523 2690 : int sto = -1, idx = cntxt->idx;
524 2690 : (void) mb;
525 :
526 2690 : if (pci->argc == 3) {
527 0 : if (cntxt->user == MAL_ADMIN) {
528 0 : idx = *getArgReference_int(stk, pci, 1);
529 0 : sto = *getArgReference_int(stk, pci, 2);
530 : } else {
531 0 : throw(MAL, "clients.setsessiontimeout",
532 : SQLSTATE(42000) "Administrator rights required");
533 : }
534 : } else {
535 2690 : sto = *getArgReference_int(stk, pci, 1);
536 : }
537 2690 : if (is_int_nil(sto))
538 0 : throw(MAL, "clients.setsessiontimeout",
539 : "Session timeout cannot be NULL");
540 2690 : if (sto < 0)
541 1 : throw(MAL, "clients.setsessiontimeout",
542 : "Session timeout should be >= 0");
543 2689 : if (idx < 0 || idx > MAL_MAXCLIENTS)
544 0 : throw(MAL, "clients.setsessiontimeout", "Illegal session id %d", idx);
545 :
546 2689 : MT_lock_set(&mal_contextLock);
547 2689 : if (mal_clients[idx].mode == FREECLIENT)
548 0 : msg = createException(MAL, "clients.setsessiontimeout",
549 : "Session not active anymore");
550 : else {
551 2689 : mal_clients[idx].sessiontimeout = sto > 0 ? (lng) sto *1000000 + (GDKusec() - mal_clients[idx].session) : 0;
552 2689 : mal_clients[idx].logical_sessiontimeout = (lng) sto;
553 : }
554 2689 : MT_lock_unset(&mal_contextLock);
555 2689 : return msg;
556 : }
557 :
558 : /* Retrieve the session time out */
559 : static str
560 0 : CLTgetProfile(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
561 : {
562 0 : str *opt = getArgReference_str(stk, pci, 0);
563 0 : int *qto = getArgReference_int(stk, pci, 1);
564 0 : int *sto = getArgReference_int(stk, pci, 2);
565 0 : int *wlim = getArgReference_int(stk, pci, 3);
566 0 : int *mlim = getArgReference_int(stk, pci, 4);
567 0 : (void) mb;
568 0 : if (!(*opt = GDKstrdup(cntxt->optimizer)))
569 0 : throw(MAL, "clients.getProfile", SQLSTATE(HY013) MAL_MALLOC_FAIL);
570 0 : *qto = (int) (cntxt->qryctx.querytimeout / 1000000);
571 0 : *sto = (int) (cntxt->sessiontimeout / 1000000);
572 0 : *wlim = cntxt->workerlimit;
573 0 : *mlim = cntxt->memorylimit;
574 0 : return MAL_SUCCEED;
575 : }
576 :
577 : /* Long running queries are traced in the logger
578 : * with a message from the interpreter.
579 : * This value should be set to minutes to avoid a lengthly log */
580 : static str
581 0 : CLTsetPrintTimeout(void *ret, int *secs)
582 : {
583 0 : (void) ret;
584 0 : if (is_int_nil(*secs))
585 0 : setqptimeout(0);
586 : else
587 0 : setqptimeout((lng) *secs * 60 * 1000000);
588 0 : return MAL_SUCCEED;
589 : }
590 :
591 : static str
592 14 : CLTmd5sum(str *ret, str *pw)
593 : {
594 14 : if (strNil(*pw)) {
595 1 : *ret = GDKstrdup(str_nil);
596 : } else {
597 13 : char *mret = mcrypt_MD5Sum(*pw, strlen(*pw));
598 :
599 13 : if (!mret)
600 0 : throw(MAL, "clients.md5sum", SQLSTATE(HY013) MAL_MALLOC_FAIL);
601 13 : *ret = GDKstrdup(mret);
602 13 : free(mret);
603 : }
604 14 : if (*ret == NULL)
605 0 : throw(MAL, "clients.md5sum", SQLSTATE(HY013) MAL_MALLOC_FAIL);
606 : return MAL_SUCCEED;
607 : }
608 :
609 : static str
610 1 : CLTsha1sum(str *ret, str *pw)
611 : {
612 1 : if (strNil(*pw)) {
613 0 : *ret = GDKstrdup(str_nil);
614 : } else {
615 1 : char *mret = mcrypt_SHA1Sum(*pw, strlen(*pw));
616 :
617 1 : if (!mret)
618 0 : throw(MAL, "clients.sha1sum", SQLSTATE(HY013) MAL_MALLOC_FAIL);
619 1 : *ret = GDKstrdup(mret);
620 1 : free(mret);
621 : }
622 1 : if (*ret == NULL)
623 0 : throw(MAL, "clients.sha1sum", SQLSTATE(HY013) MAL_MALLOC_FAIL);
624 : return MAL_SUCCEED;
625 : }
626 :
627 : static str
628 1 : CLTripemd160sum(str *ret, str *pw)
629 : {
630 1 : if (strNil(*pw)) {
631 0 : *ret = GDKstrdup(str_nil);
632 : } else {
633 1 : char *mret = mcrypt_RIPEMD160Sum(*pw, strlen(*pw));
634 :
635 1 : if (!mret)
636 0 : throw(MAL, "clients.ripemd160sum", SQLSTATE(HY013) MAL_MALLOC_FAIL);
637 1 : *ret = GDKstrdup(mret);
638 1 : free(mret);
639 : }
640 1 : if (*ret == NULL)
641 0 : throw(MAL, "clients.ripemd160sum", SQLSTATE(HY013) MAL_MALLOC_FAIL);
642 : return MAL_SUCCEED;
643 : }
644 :
645 : static str
646 4 : CLTsha2sum(str *ret, str *pw, int *bits)
647 : {
648 8 : if (strNil(*pw) || is_int_nil(*bits)) {
649 0 : *ret = GDKstrdup(str_nil);
650 : } else {
651 4 : char *mret = 0;
652 4 : switch (*bits) {
653 1 : case 512:
654 1 : mret = mcrypt_SHA512Sum(*pw, strlen(*pw));
655 1 : break;
656 1 : case 384:
657 1 : mret = mcrypt_SHA384Sum(*pw, strlen(*pw));
658 1 : break;
659 1 : case 256:
660 1 : mret = mcrypt_SHA256Sum(*pw, strlen(*pw));
661 1 : break;
662 1 : case 224:
663 1 : mret = mcrypt_SHA224Sum(*pw, strlen(*pw));
664 1 : break;
665 0 : default:
666 0 : (void) mret;
667 0 : throw(ILLARG, "clients.sha2sum", "wrong number of bits "
668 : "for SHA2 sum: %d", *bits);
669 : }
670 4 : if (!mret)
671 0 : throw(MAL, "clients.sha2sum", SQLSTATE(HY013) MAL_MALLOC_FAIL);
672 4 : *ret = GDKstrdup(mret);
673 4 : free(mret);
674 : }
675 4 : if (*ret == NULL)
676 0 : throw(MAL, "clients.sha2sum", SQLSTATE(HY013) MAL_MALLOC_FAIL);
677 : return MAL_SUCCEED;
678 : }
679 :
680 : static str
681 0 : CLTbackendsum(str *ret, str *pw)
682 : {
683 0 : if (strNil(*pw)) {
684 0 : *ret = GDKstrdup(str_nil);
685 : } else {
686 0 : char *mret = mcrypt_BackendSum(*pw, strlen(*pw));
687 0 : if (mret == NULL)
688 0 : throw(MAL, "clients.backendsum", SQLSTATE(HY013) MAL_MALLOC_FAIL);
689 0 : *ret = GDKstrdup(mret);
690 0 : free(mret);
691 : }
692 0 : if (*ret == NULL)
693 0 : throw(MAL, "clients.backendsum", SQLSTATE(HY013) MAL_MALLOC_FAIL);
694 : return MAL_SUCCEED;
695 : }
696 :
697 : static str
698 5 : CLTgetUsername(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
699 : {
700 5 : str *ret = getArgReference_str(stk, pci, 0);
701 5 : (void) mb;
702 :
703 5 : *ret = GDKstrdup(cntxt->username);
704 5 : return MAL_SUCCEED;
705 : }
706 :
707 : static str
708 0 : CLTgetPasswordHash(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
709 : {
710 0 : (void) cntxt;
711 0 : (void) mb;
712 0 : (void) stk;
713 0 : (void) pci;
714 :
715 0 : throw(MAL, "clients.getPassword",
716 : SQLSTATE(0A000) PROGRAM_NYI);
717 : }
718 :
719 : static str
720 0 : CLTcheckPermission(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
721 : {
722 0 : (void) cntxt;
723 0 : (void) mb;
724 0 : (void) stk;
725 0 : (void) pci;
726 :
727 0 : throw(MAL, "clients.checkPermission",
728 : SQLSTATE(0A000) PROGRAM_NYI);
729 : }
730 :
731 : str
732 2 : CLTshutdown(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
733 : {
734 2 : str *ret = getArgReference_str(stk, pci, 0);
735 2 : int delay;
736 2 : bit force = FALSE;
737 2 : int leftover;
738 2 : char buf[1024] = { "safe to stop last connection" };
739 :
740 2 : if (pci->argc == 3)
741 1 : force = *getArgReference_bit(stk, pci, 2);
742 :
743 2 : (void) mb;
744 :
745 2 : delay = *getArgReference_bte(stk, pci, 1);
746 :
747 2 : if (cntxt->user != MAL_ADMIN)
748 0 : throw(MAL, "mal.shutdown",
749 : SQLSTATE(42000) "Administrator rights required");
750 2 : if (is_int_nil(delay))
751 : throw(MAL, "mal.shutdown", "Delay cannot be NULL");
752 2 : if (delay < 0)
753 0 : throw(MAL, "mal.shutdown", "Delay cannot be negative");
754 2 : if (is_bit_nil(force))
755 0 : throw(MAL, "mal.shutdown", "Force cannot be NULL");
756 2 : MCstopClients(cntxt);
757 2 : do {
758 2 : if ((leftover = MCactiveClients() - 1))
759 0 : MT_sleep_ms(1000);
760 2 : delay--;
761 2 : } while (delay > 0 && leftover > 1);
762 2 : if (delay == 0 && leftover > 1)
763 0 : snprintf(buf, 1024, "%d client sessions still running", leftover);
764 2 : *ret = GDKstrdup(buf);
765 2 : if (force)
766 1 : GDKprepareExit();
767 2 : if (*ret == NULL)
768 0 : throw(MAL, "mal.shutdown", SQLSTATE(HY013) MAL_MALLOC_FAIL);
769 : return MAL_SUCCEED;
770 : }
771 :
772 : str
773 26 : CLTsessions(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
774 : {
775 26 : BAT *id = NULL, *user = NULL, *login = NULL, *sessiontimeout = NULL,
776 26 : *querytimeout = NULL, *idle = NULL;
777 26 : BAT *opt = NULL, *wlimit = NULL, *mlimit = NULL;
778 26 : bat *idId = getArgReference_bat(stk, pci, 0);
779 26 : bat *userId = getArgReference_bat(stk, pci, 1);
780 26 : bat *loginId = getArgReference_bat(stk, pci, 2);
781 26 : bat *idleId = getArgReference_bat(stk, pci, 3);
782 26 : bat *optId = getArgReference_bat(stk, pci, 4);
783 26 : bat *sessiontimeoutId = getArgReference_bat(stk, pci, 5);
784 26 : bat *querytimeoutId = getArgReference_bat(stk, pci, 6);
785 26 : bat *wlimitId = getArgReference_bat(stk, pci, 7);
786 26 : bat *mlimitId = getArgReference_bat(stk, pci, 8);
787 26 : Client c;
788 26 : timestamp ret;
789 26 : int timeout;
790 26 : str msg = NULL;
791 :
792 26 : (void) cntxt;
793 26 : (void) mb;
794 :
795 26 : id = COLnew(0, TYPE_int, 0, TRANSIENT);
796 26 : user = COLnew(0, TYPE_str, 0, TRANSIENT);
797 26 : login = COLnew(0, TYPE_timestamp, 0, TRANSIENT);
798 26 : opt = COLnew(0, TYPE_str, 0, TRANSIENT);
799 26 : sessiontimeout = COLnew(0, TYPE_int, 0, TRANSIENT);
800 26 : querytimeout = COLnew(0, TYPE_int, 0, TRANSIENT);
801 26 : wlimit = COLnew(0, TYPE_int, 0, TRANSIENT);
802 26 : mlimit = COLnew(0, TYPE_int, 0, TRANSIENT);
803 26 : idle = COLnew(0, TYPE_timestamp, 0, TRANSIENT);
804 :
805 26 : if (id == NULL || user == NULL || login == NULL || sessiontimeout == NULL
806 26 : || idle == NULL || querytimeout == NULL || opt == NULL || wlimit == NULL
807 26 : || mlimit == NULL) {
808 0 : BBPreclaim(id);
809 0 : BBPreclaim(user);
810 0 : BBPreclaim(login);
811 0 : BBPreclaim(sessiontimeout);
812 0 : BBPreclaim(querytimeout);
813 0 : BBPreclaim(idle);
814 :
815 0 : BBPreclaim(opt);
816 0 : BBPreclaim(wlimit);
817 0 : BBPreclaim(mlimit);
818 0 : throw(SQL, "sql.sessions", SQLSTATE(HY013) MAL_MALLOC_FAIL);
819 : }
820 :
821 26 : MT_lock_set(&mal_contextLock);
822 :
823 1690 : for (c = mal_clients; c < mal_clients + MAL_MAXCLIENTS; c++) {
824 1664 : if (c->mode == RUNCLIENT) {
825 26 : const char *username = c->username;
826 26 : if (!username)
827 0 : username = str_nil;
828 26 : if (BUNappend(user, username, false) != GDK_SUCCEED)
829 0 : goto bailout;
830 26 : ret = timestamp_fromtime(c->login);
831 26 : if (is_timestamp_nil(ret)) {
832 0 : msg = createException(SQL, "sql.sessions",
833 : SQLSTATE(22003)
834 : "Failed to convert user logged time");
835 0 : goto bailout;
836 : }
837 26 : if (BUNappend(id, &c->idx, false) != GDK_SUCCEED)
838 0 : goto bailout;
839 26 : if (BUNappend(login, &ret, false) != GDK_SUCCEED)
840 0 : goto bailout;
841 26 : timeout = (int) (c->logical_sessiontimeout);
842 26 : if (BUNappend(sessiontimeout, &timeout, false) != GDK_SUCCEED)
843 0 : goto bailout;
844 26 : timeout = (int) (c->qryctx.querytimeout / 1000000);
845 26 : if (BUNappend(querytimeout, &timeout, false) != GDK_SUCCEED)
846 0 : goto bailout;
847 26 : if (c->idle) {
848 0 : ret = timestamp_fromtime(c->idle);
849 0 : if (is_timestamp_nil(ret)) {
850 0 : msg = createException(SQL, "sql.sessions",
851 : SQLSTATE(22003)
852 : "Failed to convert user logged time");
853 0 : goto bailout;
854 : }
855 : } else
856 26 : ret = timestamp_nil;
857 26 : if (BUNappend(idle, &ret, false) != GDK_SUCCEED)
858 0 : goto bailout;
859 26 : if (BUNappend(opt, &c->optimizer, false) != GDK_SUCCEED)
860 0 : goto bailout;
861 26 : if (BUNappend(wlimit, &c->workerlimit, false) != GDK_SUCCEED)
862 0 : goto bailout;
863 26 : if (BUNappend(mlimit, &c->memorylimit, false) != GDK_SUCCEED)
864 0 : goto bailout;
865 : }
866 : }
867 26 : MT_lock_unset(&mal_contextLock);
868 26 : *idId = id->batCacheid;
869 26 : BBPkeepref(id);
870 26 : *userId = user->batCacheid;
871 26 : BBPkeepref(user);
872 26 : *loginId = login->batCacheid;
873 26 : BBPkeepref(login);
874 26 : *sessiontimeoutId = sessiontimeout->batCacheid;
875 26 : BBPkeepref(sessiontimeout);
876 26 : *querytimeoutId = querytimeout->batCacheid;
877 26 : BBPkeepref(querytimeout);
878 26 : *idleId = idle->batCacheid;
879 26 : BBPkeepref(idle);
880 :
881 26 : *optId = opt->batCacheid;
882 26 : BBPkeepref(opt);
883 26 : *wlimitId = wlimit->batCacheid;
884 26 : BBPkeepref(wlimit);
885 26 : *mlimitId = mlimit->batCacheid;
886 26 : BBPkeepref(mlimit);
887 26 : return MAL_SUCCEED;
888 :
889 0 : bailout:
890 0 : MT_lock_unset(&mal_contextLock);
891 0 : BBPunfix(id->batCacheid);
892 0 : BBPunfix(user->batCacheid);
893 0 : BBPunfix(login->batCacheid);
894 0 : BBPunfix(sessiontimeout->batCacheid);
895 0 : BBPunfix(querytimeout->batCacheid);
896 0 : BBPunfix(idle->batCacheid);
897 :
898 0 : BBPunfix(opt->batCacheid);
899 0 : BBPunfix(wlimit->batCacheid);
900 0 : BBPunfix(mlimit->batCacheid);
901 0 : return msg;
902 : }
903 :
904 : static str
905 0 : CLTgetSessionID(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
906 : {
907 0 : (void) mb;
908 0 : (void) stk;
909 0 : (void) pci;
910 0 : *getArgReference_int(stk, pci, 0) = cntxt->idx;
911 0 : return MAL_SUCCEED;
912 : }
913 :
914 : #include "mel.h"
915 : mel_func clients_init_funcs[] = {
916 : pattern("clients", "setListing", CLTsetListing, true, "Turn on/off echo of MAL instructions:\n1 - echo input,\n2 - show mal instruction,\n4 - show details of type resolutoin, \n8 - show binding information.", args(1,2, arg("",int),arg("flag",int))),
917 : pattern("clients", "getId", CLTgetClientId, false, "Return a number that uniquely represents the current client.", args(1,1, arg("",int))),
918 : pattern("clients", "getInfo", CLTInfo, false, "Pseudo bat with client attributes.", args(2,2, batarg("",str),batarg("",str))),
919 : pattern("clients", "getScenario", CLTgetScenario, false, "Retrieve current scenario name.", args(1,1, arg("",str))),
920 : pattern("clients", "setScenario", CLTsetScenario, true, "Switch to other scenario handler, return previous one.", args(1,2, arg("",str),arg("msg",str))),
921 : pattern("clients", "quit", CLTquit, true, "Terminate the client session.", args(1,1, arg("",void))),
922 : pattern("clients", "quit", CLTquit, true, "Terminate the session for a single client using a soft error.\nIt is the privilege of the console user.", args(1,2, arg("",void),arg("idx",int))),
923 : command("clients", "getLogins", CLTLogin, false, "Pseudo bat of client id and login time.", args(2,2, batarg("user",oid),batarg("start",str))),
924 : pattern("clients", "stop", CLTstop, true, "Stop the query execution at the next eligble statement.", args(0,1, arg("id",int))),
925 : pattern("clients", "suspend", CLTsuspend, true, "Put a client process to sleep for some time.\nIt will simple sleep for a second at a time, until\nthe awake bit has been set in its descriptor", args(1,2, arg("",void),arg("id",int))),
926 : pattern("clients", "wakeup", CLTwakeup, true, "Wakeup a client process", args(1,2, arg("",void),arg("id",int))),
927 : pattern("clients", "getprofile", CLTgetProfile, false, "Retrieve the profile settings for a client", args(5,5, arg("opt",str),arg("q",int),arg("s",int),arg("w",int),arg("m",int))),
928 : pattern("clients", "setQryTimeoutMicro", CLTqueryTimeoutMicro, true, "", args(1,2, arg("",void),arg("n",lng))),
929 : pattern("clients", "setquerytimeout", CLTqueryTimeout, true, "", args(1,2, arg("",void),arg("n",int))),
930 : pattern("clients", "setquerytimeout", CLTqueryTimeout, true, "A query is aborted after q seconds (q=0 means run undisturbed).", args(1,3, arg("",void),arg("sid",int),arg("n",int))),
931 : pattern("clients", "setsessiontimeout", CLTsessionTimeout, true, "", args(1,2, arg("",void),arg("n",int))),
932 : pattern("clients", "setsessiontimeout", CLTsessionTimeout, true, "Set the session timeout for a particulat session id", args(1,3, arg("",void),arg("sid",int),arg("n",int))),
933 : pattern("clients", "setoptimizer", CLTsetoptimizer, true, "", args(1,2, arg("",void),arg("opt",str))),
934 : pattern("clients", "setoptimizer", CLTsetoptimizer, true, "Set the session optimizer", args(1,3, arg("",void),arg("sid",int),arg("opt",str))),
935 : pattern("clients", "setworkerlimit", CLTsetworkerlimit, true, "", args(1,2, arg("",void),arg("n",int))),
936 : pattern("clients", "setworkerlimit", CLTsetworkerlimit, true, "Limit the number of worker threads per query", args(1,3, arg("",void),arg("sid",int),arg("n",int))),
937 : pattern("clients", "setmemorylimit", CLTsetmemorylimit, true, "", args(1,2, arg("",void),arg("n",int))),
938 : pattern("clients", "setmemorylimit", CLTsetmemorylimit, true, "Limit the memory claim in MB per query", args(1,3, arg("",void),arg("sid",int),arg("n",int))),
939 : pattern("clients", "stopsession", CLTstopSession, true, "Stop a particular session", args(1,2, arg("",void),arg("sid",int))),
940 : command("clients", "setprinttimeout", CLTsetPrintTimeout, true, "Print running query every so many seconds.", args(1,2, arg("",void),arg("n",int))),
941 : pattern("clients", "shutdown", CLTshutdown, true, "", args(1,2, arg("",str),arg("delay",bte))),
942 : pattern("clients", "shutdown", CLTshutdown, true, "Close all other client connections. Return if it succeeds.\nIf forced is set then always stop the system the hard way", args(1,3, arg("",str),arg("delay",bte),arg("forced",bit))),
943 : command("clients", "md5sum", CLTmd5sum, false, "Return hex string representation of the MD5 hash of the given string", args(1,2, arg("",str),arg("pw",str))),
944 : command("clients", "sha1sum", CLTsha1sum, false, "Return hex string representation of the SHA-1 hash of the given string", args(1,2, arg("",str),arg("pw",str))),
945 : command("clients", "sha2sum", CLTsha2sum, false, "Return hex string representation of the SHA-2 hash with bits of the given string", args(1,3, arg("",str),arg("pw",str),arg("bits",int))),
946 : command("clients", "ripemd160sum", CLTripemd160sum, false, "Return hex string representation of the RIPEMD160 hash of the given string", args(1,2, arg("",str),arg("pw",str))),
947 : command("clients", "backendsum", CLTbackendsum, false, "Return hex string representation of the currently used hash of the given string", args(1,2, arg("",str),arg("pw",str))),
948 : pattern("clients", "getUsername", CLTgetUsername, false, "Return the username of the currently logged in user", args(1,1, arg("",str))),
949 : pattern("clients", "getPasswordHash", CLTgetPasswordHash, false, "Return the password hash of the given user", args(1,2, arg("",str),arg("user",str))),
950 : pattern("clients", "checkPermission", CLTcheckPermission, false, "Check permission for a user, requires hashed password (backendsum)", args(1,3, arg("",void),arg("usr",str),arg("pw",str))),
951 : pattern("clients", "current_sessionid", CLTgetSessionID, false, "return current session ID", args(1,1, arg("",int))),
952 : { .imp=NULL }
953 : };
954 : #include "mal_import.h"
955 : #ifdef _MSC_VER
956 : #undef read
957 : #pragma section(".CRT$XCU",read)
958 : #endif
959 329 : LIB_STARTUP_FUNC(init_clients_mal)
960 329 : { mal_module("clients", NULL, clients_init_funcs); }
|