1/* 2 Unix SMB/Netbios implementation. 3 Version 1.9. 4 connection claim routines 5 Copyright (C) Andrew Tridgell 1998 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20*/ 21 22#include "includes.h" 23 24 25extern fstring remote_machine; 26 27extern int DEBUGLEVEL; 28 29#ifdef WITH_UTMP 30static void utmp_yield(pid_t pid, const connection_struct *conn, int i); 31static void utmp_claim(const struct connect_record *crec, connection_struct *conn, int i); 32#endif 33 34/**************************************************************************** 35simple routines to do connection counting 36****************************************************************************/ 37BOOL yield_connection(connection_struct *conn,char *name,int max_connections) 38{ 39 struct connect_record crec; 40 pstring fname; 41 int fd; 42 pid_t mypid = getpid(); 43 int i; 44 45 DEBUG(3,("Yielding connection to %s\n",name)); 46 47 if (max_connections <= 0) 48 return(True); 49 50 memset((char *)&crec,'\0',sizeof(crec)); 51 52 pstrcpy(fname,lp_lockdir()); 53 trim_string(fname,"","/"); 54 55 pstrcat(fname,"/"); 56 pstrcat(fname,name); 57 pstrcat(fname,".LCK"); 58 59 fd = sys_open(fname,O_RDWR,0); 60 if (fd == -1) { 61 DEBUG(2,("Couldn't open lock file %s (%s)\n",fname,strerror(errno))); 62 return(False); 63 } 64 65 if (fcntl_lock(fd,SMB_F_SETLKW,0,1,F_WRLCK)==False) { 66 DEBUG(0,("ERROR: can't get lock on %s\n", fname)); 67 return False; 68 } 69 70 /* find the right spot */ 71 for (i=0;i<max_connections;i++) { 72 if (read(fd, &crec,sizeof(crec)) != sizeof(crec)) { 73 DEBUG(2,("Entry not found in lock file %s\n",fname)); 74 if (fcntl_lock(fd,SMB_F_SETLKW,0,1,F_UNLCK)==False) { 75 DEBUG(0,("ERROR: can't release lock on %s\n", fname)); 76 } 77 close(fd); 78 return(False); 79 } 80 if (crec.pid == mypid && crec.cnum == conn->cnum) 81 break; 82 } 83 84 if (crec.pid != mypid || crec.cnum != conn->cnum) { 85 if (fcntl_lock(fd,SMB_F_SETLKW,0,1,F_UNLCK)==False) { 86 DEBUG(0,("ERROR: can't release lock on %s\n", fname)); 87 } 88 close(fd); 89 DEBUG(2,("Entry not found in lock file %s\n",fname)); 90 return(False); 91 } 92 93 memset((void *)&crec,'\0',sizeof(crec)); 94 95 /* remove our mark */ 96 if (sys_lseek(fd,i*sizeof(crec),SEEK_SET) != i*sizeof(crec) || 97 write(fd, &crec,sizeof(crec)) != sizeof(crec)) { 98 DEBUG(2,("Couldn't update lock file %s (%s)\n",fname,strerror(errno))); 99 if (fcntl_lock(fd,SMB_F_SETLKW,0,1,F_UNLCK)==False) { 100 DEBUG(0,("ERROR: can't release lock on %s\n", fname)); 101 } 102 close(fd); 103 return(False); 104 } 105 106 if (fcntl_lock(fd,SMB_F_SETLKW,0,1,F_UNLCK)==False) { 107 DEBUG(0,("ERROR: can't release lock on %s\n", fname)); 108 } 109 110 DEBUG(3,("Yield successful\n")); 111 112 close(fd); 113 114#ifdef WITH_UTMP 115 utmp_yield(mypid, conn, i); 116#endif 117 118 return(True); 119} 120 121 122/**************************************************************************** 123simple routines to do connection counting 124****************************************************************************/ 125BOOL claim_connection(connection_struct *conn,char *name,int max_connections,BOOL Clear) 126{ 127 extern int Client; 128 struct connect_record crec; 129 pstring fname; 130 int fd=-1; 131 int i,foundi= -1; 132 int total_recs; 133 134 if (max_connections <= 0) 135 return(True); 136 137 DEBUG(5,("trying claim %s %s %d\n",lp_lockdir(),name,max_connections)); 138 139 pstrcpy(fname,lp_lockdir()); 140 trim_string(fname,"","/"); 141 142 if (!directory_exist(fname,NULL)) 143 mkdir(fname,0755); 144 145 pstrcat(fname,"/"); 146 pstrcat(fname,name); 147 pstrcat(fname,".LCK"); 148 149 if (!file_exist(fname,NULL)) { 150 fd = sys_open(fname,O_RDWR|O_CREAT|O_EXCL, 0644); 151 } 152 153 if (fd == -1) { 154 fd = sys_open(fname,O_RDWR,0); 155 } 156 157 if (fd == -1) { 158 DEBUG(1,("couldn't open lock file %s\n",fname)); 159 return(False); 160 } 161 162 if (fcntl_lock(fd,SMB_F_SETLKW,0,1,F_WRLCK)==False) { 163 DEBUG(0,("ERROR: can't get lock on %s\n", fname)); 164 return False; 165 } 166 167 total_recs = get_file_size(fname) / sizeof(crec); 168 169 /* find a free spot */ 170 for (i=0;i<max_connections;i++) { 171 if (i>=total_recs || 172 sys_lseek(fd,i*sizeof(crec),SEEK_SET) != i*sizeof(crec) || 173 read(fd,&crec,sizeof(crec)) != sizeof(crec)) { 174 if (foundi < 0) foundi = i; 175 break; 176 } 177 178 if (Clear && crec.pid && !process_exists(crec.pid)) { 179 if(sys_lseek(fd,i*sizeof(crec),SEEK_SET) != i*sizeof(crec)) { 180 DEBUG(0,("claim_connection: ERROR: sys_lseek failed to seek \ 181to %d\n", (int)(i*sizeof(crec)) )); 182 continue; 183 } 184 memset((void *)&crec,'\0',sizeof(crec)); 185 write(fd, &crec,sizeof(crec)); 186 if (foundi < 0) foundi = i; 187 continue; 188 } 189 if (foundi < 0 && (!crec.pid || !process_exists(crec.pid))) { 190 foundi=i; 191 if (!Clear) break; 192 } 193 } 194 195 if (foundi < 0) { 196 DEBUG(3,("no free locks in %s\n",fname)); 197 if (fcntl_lock(fd,SMB_F_SETLKW,0,1,F_UNLCK)==False) { 198 DEBUG(0,("ERROR: can't release lock on %s\n", fname)); 199 } 200 close(fd); 201 return(False); 202 } 203 204 /* fill in the crec */ 205 memset((void *)&crec,'\0',sizeof(crec)); 206 crec.magic = 0x280267; 207 crec.pid = getpid(); 208 if (conn) { 209 crec.cnum = conn->cnum; 210 crec.uid = conn->uid; 211 crec.gid = conn->gid; 212 StrnCpy(crec.name, 213 lp_servicename(SNUM(conn)),sizeof(crec.name)-1); 214 } else { 215 crec.cnum = -1; 216 } 217 crec.start = time(NULL); 218 219 StrnCpy(crec.machine,remote_machine,sizeof(crec.machine)-1); 220 StrnCpy(crec.addr,client_addr(Client),sizeof(crec.addr)-1); 221 222 /* make our mark */ 223 if (sys_lseek(fd,foundi*sizeof(crec),SEEK_SET) != foundi*sizeof(crec) || 224 write(fd, &crec,sizeof(crec)) != sizeof(crec)) { 225 if (fcntl_lock(fd,SMB_F_SETLKW,0,1,F_UNLCK)==False) { 226 DEBUG(0,("ERROR: can't release lock on %s\n", fname)); 227 } 228 close(fd); 229 return(False); 230 } 231 232 if (fcntl_lock(fd,SMB_F_SETLKW,0,1,F_UNLCK)==False) { 233 DEBUG(0,("ERROR: can't release lock on %s\n", fname)); 234 } 235 236 close(fd); 237 238#ifdef WITH_UTMP 239 utmp_claim(&crec, conn, foundi); 240#endif 241 242 return(True); 243} 244 245#ifdef WITH_UTMP 246 247/**************************************************************************** 248Reflect connection status in utmp/wtmp files. 249 T.D.Lee@durham.ac.uk September 1999 250 251Hints for porting: 252 o Always attempt to use programmatic interface (pututline() etc.) 253 o The "x" (utmpx/wtmpx; HAVE_UTMPX_H) seems preferable. 254 255OS status: 256 Solaris 2.x: Tested on 2.6 and 2.7; should be OK on other flavours. 257 T.D.Lee@durham.ac.uk 258 HPUX 9.x: Not tested. Appears not to have "x". 259 IRIX 6.5: Not tested. Appears to have "x". 260 261OS observations: 262 Almost every OS seems to have its own quirks. 263 264Notes: 265 The 4 byte 'ut_id' component is vital to distinguish connections, 266 of which there could be several hundered or even thousand. 267 Entries seem to be printable characters, with optional NULL pads. 268 269 We need to be distinct from other entries in utmp/wtmp. 270 271 Observed things: therefore avoid them. Add to this list please. 272 From Solaris 2.x (because that's what I have): 273 'sN' : run-levels; N: [0-9] 274 'co' : console 275 'CC' : arbitrary things; C: [a-z] 276 'rXNN' : rlogin; N: [0-9]; X: [0-9a-z] 277 'tXNN' : rlogin; N: [0-9]; X: [0-9a-z] 278 '/NNN' : Solaris CDE 279 'ftpZ' : ftp (Z is the number 255, aka 0377, aka 0xff) 280 Mostly a record uses the same 'ut_id' in both "utmp" and "wtmp", 281 but differences have been seen. 282 283 Arbitrarily I have chosen to use a distinctive 'SM' for the 284 first two bytes. 285 286 The remaining two encode the connection number used in samba locking 287 functions "claim_connection() and "yield_connection()". This seems 288 to be a "nicely behaved" number: starting from 0 then working up 289 looking for an available slot. 290 291****************************************************************************/ 292 293#include <utmp.h> 294 295/* 296 * Apparently AIX has utmpx.h but doesn't implement it. 297 * The test for this ought to be (a) more automatic (b) elsewhere. 298 */ 299#if defined (AIX) 300#undef HAVE_UTMPX_H 301#endif 302 303#ifdef HAVE_UTMPX_H 304#include <utmpx.h> 305#endif 306 307static const char *ut_id_encstr = 308 "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 309 310static 311int 312ut_id_encode(int i, char *fourbyte) 313{ 314 int nbase; 315 316 fourbyte[0] = 'S'; 317 fourbyte[1] = 'M'; 318 319/* 320 * Encode remaining 2 bytes from 'i'. 321 * 'ut_id_encstr' is the character set on which modulo arithmetic is done. 322 * Example: digits would produce the base-10 numbers from '001'. 323 */ 324 nbase = strlen(ut_id_encstr); 325 326 fourbyte[3] = ut_id_encstr[i % nbase]; 327 i /= nbase; 328 fourbyte[2] = ut_id_encstr[i % nbase]; 329 i /= nbase; 330 331 return(i); /* 0: good; else overflow */ 332} 333 334/* 335 * Fill in a utmp (not utmpx) template 336 */ 337static int utmp_fill(struct utmp *u, const connection_struct *conn, pid_t pid, 338 int i, pstring host) 339{ 340#if defined(HAVE_UT_UT_TIME) 341 struct timeval timeval; 342#endif /* defined(HAVE_UT_UT_TIME) */ 343 int rc = 0; 344 345#if defined(HAVE_UT_UT_USER) 346 pstrcpy(u->ut_user, conn->user); 347#endif /* defined(HAVE_UT_UT_USER) */ 348 349#if defined(HAVE_UT_UT_NAME) 350 pstrcpy(u->ut_name, conn->user); 351#endif /* defined(HAVE_UT_UT_NAME) */ 352 353 slprintf(u->ut_line, 12, "smb/%d", i); 354 355 u->ut_pid = pid; 356 357#if defined(HAVE_UT_UT_TIME) 358 gettimeofday(&timeval, NULL); 359 u->ut_time = timeval.tv_sec; 360#endif /* defined(HAVE_UT_UT_TIME) */ 361 362#if defined(HAVE_UT_UT_TV) 363 gettimeofday(&timeval, NULL); 364 u->ut_tv = timeval; 365#endif /* defined(HAVE_UT_UT_TV) */ 366 367#if defined(HAVE_UT_UT_HOST) 368 if (host) { 369 pstrcpy(u->ut_host, host); 370 } 371#endif /* defined(HAVE_UT_UT_HOST) */ 372 373#if defined(HAVE_UT_UT_ID) 374 rc = ut_id_encode(i, u->ut_id); 375#endif /* defined(HAVE_UT_UT_ID) */ 376 377 return(rc); 378} 379 380/* 381 * Default paths to various {u,w}tmp{,x} files 382 */ 383#ifdef HAVE_UTMPX_H 384 385static char *ux_pathname = 386# if defined (UTMPX_FILE) 387 UTMPX_FILE ; 388# elif defined (_UTMPX_FILE) 389 _UTMPX_FILE ; 390# elif defined (_PATH_UTMPX) 391 _PATH_UTMPX ; 392# else 393 "" ; 394# endif 395 396static char *wx_pathname = 397# if defined (WTMPX_FILE) 398 WTMPX_FILE ; 399# elif defined (_WTMPX_FILE) 400 _WTMPX_FILE ; 401# elif defined (_PATH_WTMPX) 402 _PATH_WTMPX ; 403# else 404 "" ; 405# endif 406 407#endif /* HAVE_UTMPX_H */ 408 409static char *ut_pathname = 410# if defined (UTMP_FILE) 411 UTMP_FILE ; 412# elif defined (_UTMP_FILE) 413 _UTMP_FILE ; 414# elif defined (_PATH_UTMP) 415 _PATH_UTMP ; 416# else 417 "" ; 418# endif 419 420static char *wt_pathname = 421# if defined (WTMP_FILE) 422 WTMP_FILE ; 423# elif defined (_WTMP_FILE) 424 _WTMP_FILE ; 425# elif defined (_PATH_WTMP) 426 _PATH_WTMP ; 427# else 428 "" ; 429# endif 430 431/* 432 * Get name of {u,w}tmp{,x} file. 433 * return: fname contains filename 434 * Possibly empty if this code not yet ported to this system. 435 * 436 * utmp{,x}: try "utmp dir", then default (a define) 437 * wtmp{,x}: try "wtmp dir", then "utmp dir", then default (a define) 438 */ 439static void uw_pathname(pstring fname, const char *uw_name, const char *uw_default) 440{ 441 pstring dirname; 442 443 pstrcpy(dirname, ""); 444 445 /* For w-files, first look for explicit "wtmp dir" */ 446 if (uw_name[0] == 'w') { 447 pstrcpy(dirname,lp_wtmpdir()); 448 trim_string(dirname,"","/"); 449 } 450 451 /* For u-files and non-explicit w-dir, look for "utmp dir" */ 452 if (dirname == 0 || strlen(dirname) == 0) { 453 pstrcpy(dirname,lp_utmpdir()); 454 trim_string(dirname,"","/"); 455 } 456 457 /* If explicit directory above, use it */ 458 if (dirname != 0 && strlen(dirname) != 0) { 459 pstrcpy(fname, dirname); 460 pstrcat(fname, "/"); 461 pstrcat(fname, uw_name); 462 return; 463 } 464 465 /* No explicit directory: attempt to use default paths */ 466 if (strlen(uw_default) == 0) { 467 /* No explicit setting, no known default. 468 * Has it yet been ported to this OS? 469 */ 470 DEBUG(2,("uw_pathname: unable to determine pathname\n")); 471 } 472 pstrcpy(fname, uw_default); 473} 474 475static void utmp_update(const struct utmp *u, pstring host) 476{ 477 pstring fname; 478 479#ifdef HAVE_UTMPX_H 480 struct utmpx ux, *uxrc; 481 482 getutmpx(u, &ux); 483 if (host) { 484#if defined(HAVE_UX_UT_SYSLEN) 485 ux.ut_syslen = strlen(host); 486#endif /* defined(HAVE_UX_UT_SYSLEN) */ 487 pstrcpy(ux.ut_host, host); 488 } 489 490 uw_pathname(fname, "utmpx", ux_pathname); 491 DEBUG(2,("utmp_update: fname:%s\n", fname)); 492 if (strlen(fname) != 0) { 493 utmpxname(fname); 494 } 495 setutxent(); 496 uxrc = pututxline(&ux); 497 endutxent(); 498 if (uxrc == NULL) { 499 DEBUG(2,("utmp_update: pututxline() failed\n")); 500 return; 501 } 502 503 uw_pathname(fname, "wtmpx", wx_pathname); 504 DEBUG(2,("utmp_update: fname:%s\n", fname)); 505 if (strlen(fname) != 0) { 506 updwtmpx(fname, &ux); 507 } 508#else 509 uw_pathname(fname, "utmp", ut_pathname); 510 DEBUG(2,("utmp_update: fname:%s\n", fname)); 511 if (strlen(fname) != 0) { 512 utmpname(fname); 513 } 514 setutent(); 515 pututline(u); 516 endutent(); 517 518 uw_pathname(fname, "wtmp", wt_pathname); 519 520 /* *** Hmmm. Appending wtmp (as distinct from overwriting utmp) has 521 me baffled. How is it to be done? *** */ 522#endif 523} 524 525static void utmp_yield(pid_t pid, const connection_struct *conn, int i) 526{ 527 struct utmp u; 528 int nopen; 529 530 if (! lp_utmp(SNUM(conn))) { 531 DEBUG(2,("utmp_yield: lp_utmp() NULL\n")); 532 return; 533 } 534 535 nopen = conn_num_open(); 536 DEBUG(2,("utmp_yield: conn: user:%s cnum:%d i:%d (nopen:%d)\n", 537 conn->user, conn->cnum, i, nopen)); 538 539 if (lp_utmp_consolidate() && nopen > 1) { 540 DEBUG(2,("utmp_yield: utmp consolidate: %d entries open\n", nopen)); 541 return; 542 } 543 544 memset((char *)&u, '\0', sizeof(struct utmp)); 545 u.ut_type = DEAD_PROCESS; 546 u.ut_exit.e_termination = 0; 547 u.ut_exit.e_exit = 0; 548 if (utmp_fill(&u, conn, pid, i, NULL) == 0) { 549 utmp_update(&u, NULL); 550 } 551} 552 553static void utmp_claim(const struct connect_record *crec, connection_struct *conn, int i) 554{ 555 struct utmp u; 556 pstring host; 557 int nopen; 558 559 if (conn == NULL) { 560 DEBUG(2,("utmp_claim: conn NULL\n")); 561 return; 562 } 563 564 if (! lp_utmp(SNUM(conn))) { 565 DEBUG(2,("utmp_claim: lp_utmp() NULL\n")); 566 return; 567 } 568 569 nopen = conn_num_open(); 570 if (lp_utmp_consolidate() && nopen > 1) { 571 DEBUG(2,("utmp_claim: utmp consolidate: %d entries open\n", nopen)); 572 return; 573 } 574 575 pstrcpy(host, lp_utmp_hostname()); 576 if (host == 0 || strlen(host) == 0) { 577 pstrcpy(host, crec->machine); 578 } 579 else { 580 /* explicit "utmp host": expand for any "%" variables */ 581 standard_sub(conn, host); 582 } 583 584 nopen = conn_num_open(); 585 DEBUG(2,("utmp_claim: conn: user:%s cnum:%d i:%d (nopen:%d)\n", 586 conn->user, conn->cnum, i, nopen)); 587 DEBUG(2,("utmp_claim: crec: pid:%d, cnum:%d name:%s addr:%s mach:%s host:%s\n", 588 crec->pid, crec->cnum, crec->name, crec->addr, crec->machine, host)); 589 590 591 memset((char *)&u, '\0', sizeof(struct utmp)); 592 u.ut_type = USER_PROCESS; 593 if (utmp_fill(&u, conn, crec->pid, i, host) == 0) { 594 utmp_update(&u, host); 595 } 596} 597 598#endif 599