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.endtime = 1; /* stop client now */
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.endtime = 1; /* stop client now */
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].querytimeout = timeout_micro;
482 18 : QryCtx *qry_ctx = MT_thread_get_qry_ctx();
483 18 : if (qry_ctx) {
484 18 : qry_ctx->endtime = qry_ctx->starttime && timeout_micro ? qry_ctx->starttime + timeout_micro : 0;
485 : }
486 : }
487 18 : MT_lock_unset(&mal_contextLock);
488 18 : return msg;
489 : }
490 :
491 : // set query timeout based in microseconds
492 : static str
493 2 : CLTqueryTimeoutMicro(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
494 : {
495 2 : str msg = MAL_SUCCEED;
496 2 : int idx = cntxt->idx;
497 2 : lng qto = *getArgReference_lng(stk, pci, 1);
498 2 : (void) mb;
499 :
500 2 : if (is_lng_nil(qto))
501 0 : throw(MAL, "clients.queryTimeout", "Query timeout cannot be NULL");
502 2 : if (qto < 0)
503 0 : throw(MAL, "clients.queryTimeout", "Query timeout should be >= 0");
504 :
505 2 : MT_lock_set(&mal_contextLock);
506 2 : if (mal_clients[idx].mode == FREECLIENT)
507 0 : msg = createException(MAL, "clients.queryTimeout",
508 : "Session not active anymore");
509 : else {
510 2 : mal_clients[idx].querytimeout = qto;
511 2 : QryCtx *qry_ctx = MT_thread_get_qry_ctx();
512 2 : if (qry_ctx) {
513 2 : qry_ctx->endtime = qry_ctx->starttime && qto ? qry_ctx->starttime + qto : 0;
514 : }
515 : }
516 2 : MT_lock_unset(&mal_contextLock);
517 2 : return msg;
518 : }
519 :
520 : /* Set the current session timeout in seconds */
521 : static str
522 2480 : CLTsessionTimeout(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
523 : {
524 2480 : str msg = MAL_SUCCEED;
525 2480 : int sto = -1, idx = cntxt->idx;
526 2480 : (void) mb;
527 :
528 2480 : if (pci->argc == 3) {
529 0 : if (cntxt->user == MAL_ADMIN) {
530 0 : idx = *getArgReference_int(stk, pci, 1);
531 0 : sto = *getArgReference_int(stk, pci, 2);
532 : } else {
533 0 : throw(MAL, "clients.setsessiontimeout",
534 : SQLSTATE(42000) "Administrator rights required");
535 : }
536 : } else {
537 2480 : sto = *getArgReference_int(stk, pci, 1);
538 : }
539 2480 : if (is_int_nil(sto))
540 0 : throw(MAL, "clients.setsessiontimeout",
541 : "Session timeout cannot be NULL");
542 2480 : if (sto < 0)
543 1 : throw(MAL, "clients.setsessiontimeout",
544 : "Session timeout should be >= 0");
545 2479 : if (idx < 0 || idx > MAL_MAXCLIENTS)
546 0 : throw(MAL, "clients.setsessiontimeout", "Illegal session id %d", idx);
547 :
548 2479 : MT_lock_set(&mal_contextLock);
549 2479 : if (mal_clients[idx].mode == FREECLIENT)
550 0 : msg = createException(MAL, "clients.setsessiontimeout",
551 : "Session not active anymore");
552 : else {
553 2479 : mal_clients[idx].sessiontimeout = sto > 0 ? (lng) sto *1000000 + (GDKusec() - mal_clients[idx].session) : 0;
554 2479 : mal_clients[idx].logical_sessiontimeout = (lng) sto;
555 : }
556 2479 : MT_lock_unset(&mal_contextLock);
557 2479 : return msg;
558 : }
559 :
560 : /* Retrieve the session time out */
561 : static str
562 0 : CLTgetProfile(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
563 : {
564 0 : str *opt = getArgReference_str(stk, pci, 0);
565 0 : int *qto = getArgReference_int(stk, pci, 1);
566 0 : int *sto = getArgReference_int(stk, pci, 2);
567 0 : int *wlim = getArgReference_int(stk, pci, 3);
568 0 : int *mlim = getArgReference_int(stk, pci, 4);
569 0 : (void) mb;
570 0 : if (!(*opt = GDKstrdup(cntxt->optimizer)))
571 0 : throw(MAL, "clients.getProfile", SQLSTATE(HY013) MAL_MALLOC_FAIL);
572 0 : *qto = (int) (cntxt->querytimeout / 1000000);
573 0 : *sto = (int) (cntxt->sessiontimeout / 1000000);
574 0 : *wlim = cntxt->workerlimit;
575 0 : *mlim = cntxt->memorylimit;
576 0 : return MAL_SUCCEED;
577 : }
578 :
579 : /* Long running queries are traced in the logger
580 : * with a message from the interpreter.
581 : * This value should be set to minutes to avoid a lengthly log */
582 : static str
583 0 : CLTsetPrintTimeout(void *ret, int *secs)
584 : {
585 0 : (void) ret;
586 0 : if (is_int_nil(*secs))
587 0 : setqptimeout(0);
588 : else
589 0 : setqptimeout((lng) *secs * 60 * 1000000);
590 0 : return MAL_SUCCEED;
591 : }
592 :
593 : static str
594 14 : CLTmd5sum(str *ret, str *pw)
595 : {
596 14 : if (strNil(*pw)) {
597 1 : *ret = GDKstrdup(str_nil);
598 : } else {
599 13 : char *mret = mcrypt_MD5Sum(*pw, strlen(*pw));
600 :
601 13 : if (!mret)
602 0 : throw(MAL, "clients.md5sum", SQLSTATE(HY013) MAL_MALLOC_FAIL);
603 13 : *ret = GDKstrdup(mret);
604 13 : free(mret);
605 : }
606 14 : if (*ret == NULL)
607 0 : throw(MAL, "clients.md5sum", SQLSTATE(HY013) MAL_MALLOC_FAIL);
608 : return MAL_SUCCEED;
609 : }
610 :
611 : static str
612 1 : CLTsha1sum(str *ret, str *pw)
613 : {
614 1 : if (strNil(*pw)) {
615 0 : *ret = GDKstrdup(str_nil);
616 : } else {
617 1 : char *mret = mcrypt_SHA1Sum(*pw, strlen(*pw));
618 :
619 1 : if (!mret)
620 0 : throw(MAL, "clients.sha1sum", SQLSTATE(HY013) MAL_MALLOC_FAIL);
621 1 : *ret = GDKstrdup(mret);
622 1 : free(mret);
623 : }
624 1 : if (*ret == NULL)
625 0 : throw(MAL, "clients.sha1sum", SQLSTATE(HY013) MAL_MALLOC_FAIL);
626 : return MAL_SUCCEED;
627 : }
628 :
629 : static str
630 1 : CLTripemd160sum(str *ret, str *pw)
631 : {
632 1 : if (strNil(*pw)) {
633 0 : *ret = GDKstrdup(str_nil);
634 : } else {
635 1 : char *mret = mcrypt_RIPEMD160Sum(*pw, strlen(*pw));
636 :
637 1 : if (!mret)
638 0 : throw(MAL, "clients.ripemd160sum", SQLSTATE(HY013) MAL_MALLOC_FAIL);
639 1 : *ret = GDKstrdup(mret);
640 1 : free(mret);
641 : }
642 1 : if (*ret == NULL)
643 0 : throw(MAL, "clients.ripemd160sum", SQLSTATE(HY013) MAL_MALLOC_FAIL);
644 : return MAL_SUCCEED;
645 : }
646 :
647 : static str
648 4 : CLTsha2sum(str *ret, str *pw, int *bits)
649 : {
650 8 : if (strNil(*pw) || is_int_nil(*bits)) {
651 0 : *ret = GDKstrdup(str_nil);
652 : } else {
653 4 : char *mret = 0;
654 4 : switch (*bits) {
655 1 : case 512:
656 1 : mret = mcrypt_SHA512Sum(*pw, strlen(*pw));
657 1 : break;
658 1 : case 384:
659 1 : mret = mcrypt_SHA384Sum(*pw, strlen(*pw));
660 1 : break;
661 1 : case 256:
662 1 : mret = mcrypt_SHA256Sum(*pw, strlen(*pw));
663 1 : break;
664 1 : case 224:
665 1 : mret = mcrypt_SHA224Sum(*pw, strlen(*pw));
666 1 : break;
667 0 : default:
668 0 : (void) mret;
669 0 : throw(ILLARG, "clients.sha2sum", "wrong number of bits "
670 : "for SHA2 sum: %d", *bits);
671 : }
672 4 : if (!mret)
673 0 : throw(MAL, "clients.sha2sum", SQLSTATE(HY013) MAL_MALLOC_FAIL);
674 4 : *ret = GDKstrdup(mret);
675 4 : free(mret);
676 : }
677 4 : if (*ret == NULL)
678 0 : throw(MAL, "clients.sha2sum", SQLSTATE(HY013) MAL_MALLOC_FAIL);
679 : return MAL_SUCCEED;
680 : }
681 :
682 : static str
683 0 : CLTbackendsum(str *ret, str *pw)
684 : {
685 0 : if (strNil(*pw)) {
686 0 : *ret = GDKstrdup(str_nil);
687 : } else {
688 0 : char *mret = mcrypt_BackendSum(*pw, strlen(*pw));
689 0 : if (mret == NULL)
690 0 : throw(MAL, "clients.backendsum", SQLSTATE(HY013) MAL_MALLOC_FAIL);
691 0 : *ret = GDKstrdup(mret);
692 0 : free(mret);
693 : }
694 0 : if (*ret == NULL)
695 0 : throw(MAL, "clients.backendsum", SQLSTATE(HY013) MAL_MALLOC_FAIL);
696 : return MAL_SUCCEED;
697 : }
698 :
699 : static str
700 5 : CLTgetUsername(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
701 : {
702 5 : str *ret = getArgReference_str(stk, pci, 0);
703 5 : (void) mb;
704 :
705 5 : *ret = GDKstrdup(cntxt->username);
706 5 : return MAL_SUCCEED;
707 : }
708 :
709 : static str
710 0 : CLTgetPasswordHash(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
711 : {
712 0 : (void) cntxt;
713 0 : (void) mb;
714 0 : (void) stk;
715 0 : (void) pci;
716 :
717 0 : throw(MAL, "clients.getPassword",
718 : SQLSTATE(0A000) PROGRAM_NYI);
719 : }
720 :
721 : static str
722 0 : CLTcheckPermission(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
723 : {
724 0 : (void) cntxt;
725 0 : (void) mb;
726 0 : (void) stk;
727 0 : (void) pci;
728 :
729 0 : throw(MAL, "clients.checkPermission",
730 : SQLSTATE(0A000) PROGRAM_NYI);
731 : }
732 :
733 : str
734 2 : CLTshutdown(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
735 : {
736 2 : str *ret = getArgReference_str(stk, pci, 0);
737 2 : int delay;
738 2 : bit force = FALSE;
739 2 : int leftover;
740 2 : char buf[1024] = { "safe to stop last connection" };
741 :
742 2 : if (pci->argc == 3)
743 1 : force = *getArgReference_bit(stk, pci, 2);
744 :
745 2 : (void) mb;
746 :
747 2 : delay = *getArgReference_bte(stk, pci, 1);
748 :
749 2 : if (cntxt->user != MAL_ADMIN)
750 0 : throw(MAL, "mal.shutdown",
751 : SQLSTATE(42000) "Administrator rights required");
752 2 : if (is_int_nil(delay))
753 : throw(MAL, "mal.shutdown", "Delay cannot be NULL");
754 2 : if (delay < 0)
755 0 : throw(MAL, "mal.shutdown", "Delay cannot be negative");
756 2 : if (is_bit_nil(force))
757 0 : throw(MAL, "mal.shutdown", "Force cannot be NULL");
758 2 : MCstopClients(cntxt);
759 2 : do {
760 2 : if ((leftover = MCactiveClients() - 1))
761 0 : MT_sleep_ms(1000);
762 2 : delay--;
763 2 : } while (delay > 0 && leftover > 1);
764 2 : if (delay == 0 && leftover > 1)
765 0 : snprintf(buf, 1024, "%d client sessions still running", leftover);
766 2 : *ret = GDKstrdup(buf);
767 2 : if (force)
768 1 : GDKprepareExit();
769 2 : if (*ret == NULL)
770 0 : throw(MAL, "mal.shutdown", SQLSTATE(HY013) MAL_MALLOC_FAIL);
771 : return MAL_SUCCEED;
772 : }
773 :
774 : str
775 25 : CLTsessions(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
776 : {
777 25 : BAT *id = NULL, *user = NULL, *login = NULL, *sessiontimeout = NULL,
778 25 : *querytimeout = NULL, *idle = NULL;
779 25 : BAT *opt = NULL, *wlimit = NULL, *mlimit = NULL;
780 25 : bat *idId = getArgReference_bat(stk, pci, 0);
781 25 : bat *userId = getArgReference_bat(stk, pci, 1);
782 25 : bat *loginId = getArgReference_bat(stk, pci, 2);
783 25 : bat *idleId = getArgReference_bat(stk, pci, 3);
784 25 : bat *optId = getArgReference_bat(stk, pci, 4);
785 25 : bat *sessiontimeoutId = getArgReference_bat(stk, pci, 5);
786 25 : bat *querytimeoutId = getArgReference_bat(stk, pci, 6);
787 25 : bat *wlimitId = getArgReference_bat(stk, pci, 7);
788 25 : bat *mlimitId = getArgReference_bat(stk, pci, 8);
789 25 : Client c;
790 25 : timestamp ret;
791 25 : int timeout;
792 25 : str msg = NULL;
793 :
794 25 : (void) cntxt;
795 25 : (void) mb;
796 :
797 25 : id = COLnew(0, TYPE_int, 0, TRANSIENT);
798 25 : user = COLnew(0, TYPE_str, 0, TRANSIENT);
799 25 : login = COLnew(0, TYPE_timestamp, 0, TRANSIENT);
800 25 : opt = COLnew(0, TYPE_str, 0, TRANSIENT);
801 25 : sessiontimeout = COLnew(0, TYPE_int, 0, TRANSIENT);
802 25 : querytimeout = COLnew(0, TYPE_int, 0, TRANSIENT);
803 25 : wlimit = COLnew(0, TYPE_int, 0, TRANSIENT);
804 25 : mlimit = COLnew(0, TYPE_int, 0, TRANSIENT);
805 25 : idle = COLnew(0, TYPE_timestamp, 0, TRANSIENT);
806 :
807 25 : if (id == NULL || user == NULL || login == NULL || sessiontimeout == NULL
808 25 : || idle == NULL || querytimeout == NULL || opt == NULL || wlimit == NULL
809 25 : || mlimit == NULL) {
810 0 : BBPreclaim(id);
811 0 : BBPreclaim(user);
812 0 : BBPreclaim(login);
813 0 : BBPreclaim(sessiontimeout);
814 0 : BBPreclaim(querytimeout);
815 0 : BBPreclaim(idle);
816 :
817 0 : BBPreclaim(opt);
818 0 : BBPreclaim(wlimit);
819 0 : BBPreclaim(mlimit);
820 0 : throw(SQL, "sql.sessions", SQLSTATE(HY013) MAL_MALLOC_FAIL);
821 : }
822 :
823 25 : MT_lock_set(&mal_contextLock);
824 :
825 1625 : for (c = mal_clients; c < mal_clients + MAL_MAXCLIENTS; c++) {
826 1600 : if (c->mode == RUNCLIENT) {
827 25 : const char *username = c->username;
828 25 : if (!username)
829 0 : username = str_nil;
830 25 : if (BUNappend(user, username, false) != GDK_SUCCEED)
831 0 : goto bailout;
832 25 : ret = timestamp_fromtime(c->login);
833 25 : if (is_timestamp_nil(ret)) {
834 0 : msg = createException(SQL, "sql.sessions",
835 : SQLSTATE(22003)
836 : "Failed to convert user logged time");
837 0 : goto bailout;
838 : }
839 25 : if (BUNappend(id, &c->idx, false) != GDK_SUCCEED)
840 0 : goto bailout;
841 25 : if (BUNappend(login, &ret, false) != GDK_SUCCEED)
842 0 : goto bailout;
843 25 : timeout = (int) (c->logical_sessiontimeout);
844 25 : if (BUNappend(sessiontimeout, &timeout, false) != GDK_SUCCEED)
845 0 : goto bailout;
846 25 : timeout = (int) (c->querytimeout / 1000000);
847 25 : if (BUNappend(querytimeout, &timeout, false) != GDK_SUCCEED)
848 0 : goto bailout;
849 25 : if (c->idle) {
850 0 : ret = timestamp_fromtime(c->idle);
851 0 : if (is_timestamp_nil(ret)) {
852 0 : msg = createException(SQL, "sql.sessions",
853 : SQLSTATE(22003)
854 : "Failed to convert user logged time");
855 0 : goto bailout;
856 : }
857 : } else
858 25 : ret = timestamp_nil;
859 25 : if (BUNappend(idle, &ret, false) != GDK_SUCCEED)
860 0 : goto bailout;
861 25 : if (BUNappend(opt, &c->optimizer, false) != GDK_SUCCEED)
862 0 : goto bailout;
863 25 : if (BUNappend(wlimit, &c->workerlimit, false) != GDK_SUCCEED)
864 0 : goto bailout;
865 25 : if (BUNappend(mlimit, &c->memorylimit, false) != GDK_SUCCEED)
866 0 : goto bailout;
867 : }
868 : }
869 25 : MT_lock_unset(&mal_contextLock);
870 25 : *idId = id->batCacheid;
871 25 : BBPkeepref(id);
872 25 : *userId = user->batCacheid;
873 25 : BBPkeepref(user);
874 25 : *loginId = login->batCacheid;
875 25 : BBPkeepref(login);
876 25 : *sessiontimeoutId = sessiontimeout->batCacheid;
877 25 : BBPkeepref(sessiontimeout);
878 25 : *querytimeoutId = querytimeout->batCacheid;
879 25 : BBPkeepref(querytimeout);
880 25 : *idleId = idle->batCacheid;
881 25 : BBPkeepref(idle);
882 :
883 25 : *optId = opt->batCacheid;
884 25 : BBPkeepref(opt);
885 25 : *wlimitId = wlimit->batCacheid;
886 25 : BBPkeepref(wlimit);
887 25 : *mlimitId = mlimit->batCacheid;
888 25 : BBPkeepref(mlimit);
889 25 : return MAL_SUCCEED;
890 :
891 0 : bailout:
892 0 : MT_lock_unset(&mal_contextLock);
893 0 : BBPunfix(id->batCacheid);
894 0 : BBPunfix(user->batCacheid);
895 0 : BBPunfix(login->batCacheid);
896 0 : BBPunfix(sessiontimeout->batCacheid);
897 0 : BBPunfix(querytimeout->batCacheid);
898 0 : BBPunfix(idle->batCacheid);
899 :
900 0 : BBPunfix(opt->batCacheid);
901 0 : BBPunfix(wlimit->batCacheid);
902 0 : BBPunfix(mlimit->batCacheid);
903 0 : return msg;
904 : }
905 :
906 : static str
907 0 : CLTgetSessionID(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
908 : {
909 0 : (void) mb;
910 0 : (void) stk;
911 0 : (void) pci;
912 0 : *getArgReference_int(stk, pci, 0) = cntxt->idx;
913 0 : return MAL_SUCCEED;
914 : }
915 :
916 : #include "mel.h"
917 : mel_func clients_init_funcs[] = {
918 : 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))),
919 : pattern("clients", "getId", CLTgetClientId, false, "Return a number that uniquely represents the current client.", args(1,1, arg("",int))),
920 : pattern("clients", "getInfo", CLTInfo, false, "Pseudo bat with client attributes.", args(2,2, batarg("",str),batarg("",str))),
921 : pattern("clients", "getScenario", CLTgetScenario, false, "Retrieve current scenario name.", args(1,1, arg("",str))),
922 : pattern("clients", "setScenario", CLTsetScenario, true, "Switch to other scenario handler, return previous one.", args(1,2, arg("",str),arg("msg",str))),
923 : pattern("clients", "quit", CLTquit, true, "Terminate the client session.", args(1,1, arg("",void))),
924 : 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))),
925 : command("clients", "getLogins", CLTLogin, false, "Pseudo bat of client id and login time.", args(2,2, batarg("user",oid),batarg("start",str))),
926 : pattern("clients", "stop", CLTstop, true, "Stop the query execution at the next eligble statement.", args(0,1, arg("id",int))),
927 : 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))),
928 : pattern("clients", "wakeup", CLTwakeup, true, "Wakeup a client process", args(1,2, arg("",void),arg("id",int))),
929 : 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))),
930 : pattern("clients", "setQryTimeoutMicro", CLTqueryTimeoutMicro, true, "", args(1,2, arg("",void),arg("n",lng))),
931 : pattern("clients", "setquerytimeout", CLTqueryTimeout, true, "", args(1,2, arg("",void),arg("n",int))),
932 : 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))),
933 : pattern("clients", "setsessiontimeout", CLTsessionTimeout, true, "", args(1,2, arg("",void),arg("n",int))),
934 : 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))),
935 : pattern("clients", "setoptimizer", CLTsetoptimizer, true, "", args(1,2, arg("",void),arg("opt",str))),
936 : pattern("clients", "setoptimizer", CLTsetoptimizer, true, "Set the session optimizer", args(1,3, arg("",void),arg("sid",int),arg("opt",str))),
937 : pattern("clients", "setworkerlimit", CLTsetworkerlimit, true, "", args(1,2, arg("",void),arg("n",int))),
938 : pattern("clients", "setworkerlimit", CLTsetworkerlimit, true, "Limit the number of worker threads per query", args(1,3, arg("",void),arg("sid",int),arg("n",int))),
939 : pattern("clients", "setmemorylimit", CLTsetmemorylimit, true, "", args(1,2, arg("",void),arg("n",int))),
940 : pattern("clients", "setmemorylimit", CLTsetmemorylimit, true, "Limit the memory claim in MB per query", args(1,3, arg("",void),arg("sid",int),arg("n",int))),
941 : pattern("clients", "stopsession", CLTstopSession, true, "Stop a particular session", args(1,2, arg("",void),arg("sid",int))),
942 : command("clients", "setprinttimeout", CLTsetPrintTimeout, true, "Print running query every so many seconds.", args(1,2, arg("",void),arg("n",int))),
943 : pattern("clients", "shutdown", CLTshutdown, true, "", args(1,2, arg("",str),arg("delay",bte))),
944 : 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))),
945 : 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))),
946 : 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))),
947 : 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))),
948 : 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))),
949 : 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))),
950 : pattern("clients", "getUsername", CLTgetUsername, false, "Return the username of the currently logged in user", args(1,1, arg("",str))),
951 : pattern("clients", "getPasswordHash", CLTgetPasswordHash, false, "Return the password hash of the given user", args(1,2, arg("",str),arg("user",str))),
952 : 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))),
953 : pattern("clients", "current_sessionid", CLTgetSessionID, false, "return current session ID", args(1,1, arg("",int))),
954 : { .imp=NULL }
955 : };
956 : #include "mal_import.h"
957 : #ifdef _MSC_VER
958 : #undef read
959 : #pragma section(".CRT$XCU",read)
960 : #endif
961 334 : LIB_STARTUP_FUNC(init_clients_mal)
962 334 : { mal_module("clients", NULL, clients_init_funcs); }
|