Parent Directory
|
Revision Log
Revision 38 - (view) (download) (as text)
| 1 : | mark | 38 | /* |
| 2 : | Copyright (C) 2008 - Mark Burgess | ||
| 3 : | |||
| 4 : | This file is part of Cfengine 3 - written and maintained by Mark Burgess. | ||
| 5 : | |||
| 6 : | This program is free software; you can redistribute it and/or modify it | ||
| 7 : | under the terms of the GNU General Public License as published by the | ||
| 8 : | Free Software Foundation; either version 3, or (at your option) any | ||
| 9 : | later version. | ||
| 10 : | This program is distributed in the hope that it will be useful, | ||
| 11 : | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 : | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 : | GNU General Public License for more details. | ||
| 14 : | |||
| 15 : | You should have received a copy of the GNU General Public License | ||
| 16 : | |||
| 17 : | along with this program; if not, write to the Free Software | ||
| 18 : | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | ||
| 19 : | |||
| 20 : | */ | ||
| 21 : | |||
| 22 : | /*****************************************************************************/ | ||
| 23 : | /* */ | ||
| 24 : | /* File: transaction.c */ | ||
| 25 : | /* */ | ||
| 26 : | /*****************************************************************************/ | ||
| 27 : | |||
| 28 : | #include "cf3.defs.h" | ||
| 29 : | #include "cf3.extern.h" | ||
| 30 : | |||
| 31 : | /*****************************************************************************/ | ||
| 32 : | |||
| 33 : | void SummarizeTransaction(struct Promise *pp,struct FileAttr attr,char result) | ||
| 34 : | |||
| 35 : | { | ||
| 36 : | switch (result) | ||
| 37 : | { | ||
| 38 : | case CF_CHG: | ||
| 39 : | PR_REPAIRED++; | ||
| 40 : | AddAllClasses(attr.classes.change); | ||
| 41 : | break; | ||
| 42 : | |||
| 43 : | case CF_WARN: | ||
| 44 : | PR_NOTKEPT++; | ||
| 45 : | break; | ||
| 46 : | |||
| 47 : | case CF_TIMEX: | ||
| 48 : | PR_NOTKEPT++; | ||
| 49 : | AddAllClasses(attr.classes.timeout); | ||
| 50 : | break; | ||
| 51 : | |||
| 52 : | case CF_FAIL: | ||
| 53 : | PR_NOTKEPT++; | ||
| 54 : | AddAllClasses(attr.classes.failure); | ||
| 55 : | break; | ||
| 56 : | |||
| 57 : | case CF_DENIED: | ||
| 58 : | PR_NOTKEPT++; | ||
| 59 : | AddAllClasses(attr.classes.denied); | ||
| 60 : | break; | ||
| 61 : | |||
| 62 : | case CF_INTERPT: | ||
| 63 : | PR_NOTKEPT++; | ||
| 64 : | AddAllClasses(attr.classes.interrupt); | ||
| 65 : | break; | ||
| 66 : | |||
| 67 : | case CF_REGULAR: | ||
| 68 : | PR_REPAIRED++; | ||
| 69 : | break; | ||
| 70 : | |||
| 71 : | case CF_NOP: | ||
| 72 : | PR_KEPT++; | ||
| 73 : | break; | ||
| 74 : | |||
| 75 : | case CF_UNKNOWN: | ||
| 76 : | PR_KEPT++; | ||
| 77 : | break; | ||
| 78 : | } | ||
| 79 : | } | ||
| 80 : | |||
| 81 : | /*****************************************************************************/ | ||
| 82 : | |||
| 83 : | struct CfLock AcquireLock(char *operator,char *operand,char *host,time_t now,struct FileAttr attr,struct Promise *pp) | ||
| 84 : | |||
| 85 : | { unsigned int pid; | ||
| 86 : | int i, err, sum=0; | ||
| 87 : | time_t lastcompleted = 0, elapsedtime; | ||
| 88 : | char c_operator[CF_BUFSIZE],c_operand[CF_BUFSIZE],cc_operator[CF_BUFSIZE],cc_operand[CF_BUFSIZE]; | ||
| 89 : | char cflock[CF_BUFSIZE],cflast[CF_BUFSIZE],cflog[CF_BUFSIZE]; | ||
| 90 : | struct CfLock this; | ||
| 91 : | |||
| 92 : | this.last = (char *) CF_UNDEFINED; | ||
| 93 : | this.lock = (char *) CF_UNDEFINED; | ||
| 94 : | this.log = (char *) CF_UNDEFINED; | ||
| 95 : | |||
| 96 : | /* Mark this here as done if we tried ... as we have passed all class constraints now */ | ||
| 97 : | |||
| 98 : | *(pp->donep) = true; | ||
| 99 : | |||
| 100 : | if (IGNORELOCK) | ||
| 101 : | { | ||
| 102 : | return this; | ||
| 103 : | } | ||
| 104 : | |||
| 105 : | if (now == 0) | ||
| 106 : | { | ||
| 107 : | return this; | ||
| 108 : | } | ||
| 109 : | |||
| 110 : | Debug("AcquireLock(%s,%s,time=%d), ExpireAfter=%d, IfElapsed=%d\n",operator,operand,now,attr.transaction.expireafter,attr.transaction.ifelapsed); | ||
| 111 : | |||
| 112 : | /* Make local copy in case CanonifyName called - not re-entrant - best fix for now */ | ||
| 113 : | |||
| 114 : | strncpy(c_operator,operator,CF_BUFSIZE-1); | ||
| 115 : | strncpy(c_operand,operand,CF_BUFSIZE-1); | ||
| 116 : | strncpy(cc_operator,CanonifyName(c_operator),CF_BUFSIZE-1); | ||
| 117 : | strncpy(cc_operand,CanonifyName(c_operand),CF_BUFSIZE-1); | ||
| 118 : | |||
| 119 : | for (i = 0; operator[i] != '\0'; i++) | ||
| 120 : | { | ||
| 121 : | sum = (CF_MACROALPHABET * sum + operator[i]) % CF_HASHTABLESIZE; | ||
| 122 : | } | ||
| 123 : | |||
| 124 : | for (i = 0; operand[i] != '\0'; i++) | ||
| 125 : | { | ||
| 126 : | sum = (CF_MACROALPHABET * sum + operand[i]) % CF_HASHTABLESIZE; | ||
| 127 : | } | ||
| 128 : | |||
| 129 : | snprintf(cflog,CF_BUFSIZE,"%s/cf3.%.40s.runlog",CFWORKDIR,host); | ||
| 130 : | snprintf(cflock,CF_BUFSIZE,"lock.%.40s.%.100s.%s.%.100s_%d",CanonifyName(host),pp->bundle,cc_operator,cc_operand,sum); | ||
| 131 : | snprintf(cflast,CF_BUFSIZE,"last.%.40s.%.100s.%s.%.100s_%d",CanonifyName(host),pp->bundle,cc_operator,cc_operand,sum); | ||
| 132 : | |||
| 133 : | /* for signal handler - not threadsafe so applies only to main thread */ | ||
| 134 : | |||
| 135 : | CFINITSTARTTIME = time(NULL); | ||
| 136 : | strcpy(CFLOCK,cflock); | ||
| 137 : | |||
| 138 : | /* Look for non-existent (old) processes */ | ||
| 139 : | |||
| 140 : | lastcompleted = FindLock(cflast); | ||
| 141 : | elapsedtime = (time_t)(now-lastcompleted) / 60; | ||
| 142 : | |||
| 143 : | if (elapsedtime < 0) | ||
| 144 : | { | ||
| 145 : | snprintf(OUTPUT,CF_BUFSIZE*2,"Another cfengine seems to have done [%s.%s] since I started (elapsed=%d)\n",operator,operand,elapsedtime); | ||
| 146 : | CfLog(cfverbose,OUTPUT,""); | ||
| 147 : | return this; | ||
| 148 : | } | ||
| 149 : | |||
| 150 : | if (elapsedtime < attr.transaction.ifelapsed) | ||
| 151 : | { | ||
| 152 : | snprintf(OUTPUT,CF_BUFSIZE*2,"Nothing promised for [%s.%s] (%u/%u minutes elapsed)\n",operator,operand,elapsedtime,attr.transaction.ifelapsed); | ||
| 153 : | CfLog(cfverbose,OUTPUT,""); | ||
| 154 : | return this; | ||
| 155 : | } | ||
| 156 : | |||
| 157 : | /* Look for existing (current) processes */ | ||
| 158 : | |||
| 159 : | lastcompleted = FindLock(cflock); | ||
| 160 : | elapsedtime = (time_t)(now-lastcompleted) / 60; | ||
| 161 : | |||
| 162 : | if (lastcompleted != 0) | ||
| 163 : | { | ||
| 164 : | if (elapsedtime >= attr.transaction.expireafter) | ||
| 165 : | { | ||
| 166 : | snprintf(OUTPUT,CF_BUFSIZE*2,"Lock %s expired (after %u/%u minutes)\n",cflock,elapsedtime,attr.transaction.expireafter); | ||
| 167 : | CfLog(cfinform,OUTPUT,""); | ||
| 168 : | |||
| 169 : | pid = FindLockPid(cflock); | ||
| 170 : | |||
| 171 : | if (pid == -1) | ||
| 172 : | { | ||
| 173 : | snprintf(OUTPUT,CF_BUFSIZE*2,"Illegal pid in corrupt lock %s - ignoring lock\n",cflock); | ||
| 174 : | CfLog(cferror,OUTPUT,""); | ||
| 175 : | } | ||
| 176 : | else | ||
| 177 : | { | ||
| 178 : | Verbose("Trying to kill expired process, pid %d\n",pid); | ||
| 179 : | |||
| 180 : | err = 0; | ||
| 181 : | |||
| 182 : | if ((err = kill(pid,SIGINT)) == -1) | ||
| 183 : | { | ||
| 184 : | sleep(1); | ||
| 185 : | err=0; | ||
| 186 : | |||
| 187 : | if ((err = kill(pid,SIGTERM)) == -1) | ||
| 188 : | { | ||
| 189 : | sleep(5); | ||
| 190 : | err=0; | ||
| 191 : | |||
| 192 : | if ((err = kill(pid,SIGKILL)) == -1) | ||
| 193 : | { | ||
| 194 : | sleep(1); | ||
| 195 : | } | ||
| 196 : | } | ||
| 197 : | } | ||
| 198 : | |||
| 199 : | if (err == 0 || errno == ESRCH) | ||
| 200 : | { | ||
| 201 : | LogLockCompletion(cflog,pid,"Lock expired, process killed",operator,operand); | ||
| 202 : | unlink(cflock); | ||
| 203 : | } | ||
| 204 : | else | ||
| 205 : | { | ||
| 206 : | snprintf(OUTPUT,CF_BUFSIZE*2,"Unable to kill expired cfagent process %d from lock %s, exiting this time..\n",pid,cflock); | ||
| 207 : | CfLog(cferror,OUTPUT,"kill"); | ||
| 208 : | |||
| 209 : | FatalError(""); | ||
| 210 : | } | ||
| 211 : | } | ||
| 212 : | } | ||
| 213 : | else | ||
| 214 : | { | ||
| 215 : | Verbose("Couldn't obtain lock for %s (already running!)\n",cflock); | ||
| 216 : | return this; | ||
| 217 : | } | ||
| 218 : | } | ||
| 219 : | |||
| 220 : | WriteLock(cflock); | ||
| 221 : | |||
| 222 : | this.lock = strdup(cflock); | ||
| 223 : | this.last = strdup(cflast); | ||
| 224 : | this.log = strdup(cflog); | ||
| 225 : | |||
| 226 : | return this; | ||
| 227 : | } | ||
| 228 : | |||
| 229 : | /************************************************************************/ | ||
| 230 : | |||
| 231 : | void YieldCurrentLock(struct CfLock this) | ||
| 232 : | |||
| 233 : | { | ||
| 234 : | if (IGNORELOCK) | ||
| 235 : | { | ||
| 236 : | return; | ||
| 237 : | } | ||
| 238 : | |||
| 239 : | if (this.lock == (char *)CF_UNDEFINED) | ||
| 240 : | { | ||
| 241 : | return; | ||
| 242 : | } | ||
| 243 : | |||
| 244 : | Debug("ReleaseCurrentLock(%s)\n",this.lock); | ||
| 245 : | |||
| 246 : | if (RemoveLock(this.lock) == -1) | ||
| 247 : | { | ||
| 248 : | Debug("Unable to remove lock %s\n",this.lock); | ||
| 249 : | free(this.last); | ||
| 250 : | free(this.lock); | ||
| 251 : | free(this.log); | ||
| 252 : | return; | ||
| 253 : | } | ||
| 254 : | |||
| 255 : | if (WriteLock(this.last) == -1) | ||
| 256 : | { | ||
| 257 : | snprintf(OUTPUT,CF_BUFSIZE*2,"Unable to create %s\n",this.last); | ||
| 258 : | CfLog(cferror,OUTPUT,"creat"); | ||
| 259 : | free(this.last); | ||
| 260 : | free(this.lock); | ||
| 261 : | free(this.log); | ||
| 262 : | return; | ||
| 263 : | } | ||
| 264 : | |||
| 265 : | LogLockCompletion(this.log,getpid(),"Lock removed normally ",this.lock,""); | ||
| 266 : | |||
| 267 : | free(this.last); | ||
| 268 : | free(this.lock); | ||
| 269 : | free(this.log); | ||
| 270 : | } | ||
| 271 : | |||
| 272 : | |||
| 273 : | /*****************************************************************************/ | ||
| 274 : | /* Level */ | ||
| 275 : | /*****************************************************************************/ | ||
| 276 : | |||
| 277 : | time_t FindLock(char *last) | ||
| 278 : | |||
| 279 : | { time_t mtime; | ||
| 280 : | |||
| 281 : | if ((mtime = FindLockTime(last)) == -1) | ||
| 282 : | { | ||
| 283 : | /* Do this to prevent deadlock loops from surviving if IfElapsed > T_sched */ | ||
| 284 : | |||
| 285 : | if (WriteLock(last) == -1) | ||
| 286 : | { | ||
| 287 : | snprintf(OUTPUT,CF_BUFSIZE*2,"Unable to lock %s\n",last); | ||
| 288 : | CfLog(cferror,OUTPUT,""); | ||
| 289 : | return 0; | ||
| 290 : | } | ||
| 291 : | |||
| 292 : | return 0; | ||
| 293 : | } | ||
| 294 : | else | ||
| 295 : | { | ||
| 296 : | return mtime; | ||
| 297 : | } | ||
| 298 : | } | ||
| 299 : | |||
| 300 : | /************************************************************************/ | ||
| 301 : | |||
| 302 : | int WriteLock(char *name) | ||
| 303 : | |||
| 304 : | { DB *dbp; | ||
| 305 : | struct LockData entry; | ||
| 306 : | |||
| 307 : | Debug("WriteLock(%s)\n",name); | ||
| 308 : | |||
| 309 : | if ((dbp = OpenLock()) == NULL) | ||
| 310 : | { | ||
| 311 : | return 0; | ||
| 312 : | } | ||
| 313 : | |||
| 314 : | DeleteDB(dbp,name); | ||
| 315 : | |||
| 316 : | entry.pid = getpid(); | ||
| 317 : | entry.time = time((time_t *)NULL); | ||
| 318 : | |||
| 319 : | #if defined HAVE_LIBPTHREAD || defined BUILDTIN_GCC_THREAD | ||
| 320 : | if (pthread_mutex_lock(&MUTEX_LOCK) != 0) | ||
| 321 : | { | ||
| 322 : | CfLog(cferror,"pthread_mutex_lock failed","pthread_mutex_lock"); | ||
| 323 : | } | ||
| 324 : | #endif | ||
| 325 : | |||
| 326 : | WriteDB(dbp,name,&entry,sizeof(entry)); | ||
| 327 : | |||
| 328 : | #if defined HAVE_LIBPTHREAD || defined BUILDTIN_GCC_THREAD | ||
| 329 : | if (pthread_mutex_unlock(&MUTEX_LOCK) != 0) | ||
| 330 : | { | ||
| 331 : | CfLog(cferror,"pthread_mutex_unlock failed","unlock"); | ||
| 332 : | } | ||
| 333 : | #endif | ||
| 334 : | |||
| 335 : | CloseLock(dbp); | ||
| 336 : | return 0; | ||
| 337 : | } | ||
| 338 : | |||
| 339 : | /*****************************************************************************/ | ||
| 340 : | |||
| 341 : | void LogLockCompletion(char *cflog,int pid,char *str,char *operator,char *operand) | ||
| 342 : | |||
| 343 : | { FILE *fp; | ||
| 344 : | char buffer[CF_MAXVARSIZE]; | ||
| 345 : | struct stat statbuf; | ||
| 346 : | time_t tim; | ||
| 347 : | |||
| 348 : | Debug("LockLogCompletion(%s)\n",str); | ||
| 349 : | |||
| 350 : | if ((fp = fopen(cflog,"a")) == NULL) | ||
| 351 : | { | ||
| 352 : | snprintf(OUTPUT,CF_BUFSIZE*2,"Can't open lock-log file %s\n",cflog); | ||
| 353 : | CfLog(cferror,OUTPUT,"fopen"); | ||
| 354 : | FatalError(""); | ||
| 355 : | } | ||
| 356 : | |||
| 357 : | if ((tim = time((time_t *)NULL)) == -1) | ||
| 358 : | { | ||
| 359 : | Debug("Cfengine: couldn't read system clock\n"); | ||
| 360 : | } | ||
| 361 : | |||
| 362 : | sprintf(buffer,"%s",ctime(&tim)); | ||
| 363 : | |||
| 364 : | Chop(buffer); | ||
| 365 : | |||
| 366 : | fprintf(fp,"%s:%s:pid=%d:%s:%s\n",buffer,str,pid,operator,operand); | ||
| 367 : | |||
| 368 : | fclose(fp); | ||
| 369 : | |||
| 370 : | if (stat(cflog,&statbuf) != -1) | ||
| 371 : | { | ||
| 372 : | if (statbuf.st_size > CFLOGSIZE) | ||
| 373 : | { | ||
| 374 : | Verbose("Rotating lock-runlog file\n"); | ||
| 375 : | RotateFiles(CFLOG,2); | ||
| 376 : | } | ||
| 377 : | } | ||
| 378 : | } | ||
| 379 : | |||
| 380 : | /*****************************************************************************/ | ||
| 381 : | |||
| 382 : | int RemoveLock(char *name) | ||
| 383 : | |||
| 384 : | { DBT key; | ||
| 385 : | DB *dbp; | ||
| 386 : | |||
| 387 : | if ((dbp = OpenLock()) == NULL) | ||
| 388 : | { | ||
| 389 : | return 0; | ||
| 390 : | } | ||
| 391 : | |||
| 392 : | #if defined HAVE_LIBPTHREAD || defined BUILDTIN_GCC_THREAD | ||
| 393 : | if (pthread_mutex_lock(&MUTEX_LOCK) != 0) | ||
| 394 : | { | ||
| 395 : | CfLog(cferror,"pthread_mutex_lock failed","pthread_mutex_lock"); | ||
| 396 : | } | ||
| 397 : | #endif | ||
| 398 : | |||
| 399 : | DeleteDB(dbp,name); | ||
| 400 : | |||
| 401 : | #if defined HAVE_LIBPTHREAD || defined BUILDTIN_GCC_THREAD | ||
| 402 : | if (pthread_mutex_unlock(&MUTEX_LOCK) != 0) | ||
| 403 : | { | ||
| 404 : | CfLog(cferror,"pthread_mutex_unlock failed","unlock"); | ||
| 405 : | } | ||
| 406 : | #endif | ||
| 407 : | |||
| 408 : | CloseLock(dbp); | ||
| 409 : | return 0; | ||
| 410 : | } | ||
| 411 : | |||
| 412 : | /************************************************************************/ | ||
| 413 : | |||
| 414 : | time_t FindLockTime(char *name) | ||
| 415 : | |||
| 416 : | { DB *dbp; | ||
| 417 : | struct LockData entry; | ||
| 418 : | |||
| 419 : | Debug("FindLockTime(%s)\n",name); | ||
| 420 : | |||
| 421 : | if ((dbp = OpenLock()) == NULL) | ||
| 422 : | { | ||
| 423 : | return -1; | ||
| 424 : | } | ||
| 425 : | |||
| 426 : | if (ReadDB(dbp,name,&entry,sizeof(entry))) | ||
| 427 : | { | ||
| 428 : | CloseLock(dbp); | ||
| 429 : | return entry.time; | ||
| 430 : | } | ||
| 431 : | else | ||
| 432 : | { | ||
| 433 : | CloseLock(dbp); | ||
| 434 : | return -1; | ||
| 435 : | } | ||
| 436 : | } | ||
| 437 : | |||
| 438 : | /************************************************************************/ | ||
| 439 : | |||
| 440 : | pid_t FindLockPid(char *name) | ||
| 441 : | |||
| 442 : | { DB *dbp; | ||
| 443 : | struct LockData entry; | ||
| 444 : | |||
| 445 : | if ((dbp = OpenLock()) == NULL) | ||
| 446 : | { | ||
| 447 : | return -1; | ||
| 448 : | } | ||
| 449 : | |||
| 450 : | if (ReadDB(dbp,name,&entry,sizeof(entry))) | ||
| 451 : | { | ||
| 452 : | CloseLock(dbp); | ||
| 453 : | return entry.pid; | ||
| 454 : | } | ||
| 455 : | else | ||
| 456 : | { | ||
| 457 : | CloseLock(dbp); | ||
| 458 : | return -1; | ||
| 459 : | } | ||
| 460 : | } | ||
| 461 : | |||
| 462 : | /************************************************************************/ | ||
| 463 : | |||
| 464 : | DB *OpenLock() | ||
| 465 : | |||
| 466 : | { char name[CF_BUFSIZE]; | ||
| 467 : | DB *dbp; | ||
| 468 : | |||
| 469 : | snprintf(name,CF_BUFSIZE,"%s/cfengine_lock_db",CFWORKDIR); | ||
| 470 : | |||
| 471 : | Debug("OpenLock(%s)\n",name); | ||
| 472 : | |||
| 473 : | if ((errno = db_create(&dbp,NULL,0)) != 0) | ||
| 474 : | { | ||
| 475 : | snprintf(OUTPUT,CF_BUFSIZE*2,"Couldn't open lock database %s\n",name); | ||
| 476 : | CfLog(cferror,OUTPUT,"db_open"); | ||
| 477 : | return NULL; | ||
| 478 : | } | ||
| 479 : | |||
| 480 : | #ifdef CF_OLD_DB | ||
| 481 : | if ((errno = (dbp->open)(dbp,name,NULL,DB_BTREE,DB_CREATE,0644)) != 0) | ||
| 482 : | #else | ||
| 483 : | if ((errno = (dbp->open)(dbp,NULL,name,NULL,DB_BTREE,DB_CREATE,0644)) != 0) | ||
| 484 : | #endif | ||
| 485 : | { | ||
| 486 : | snprintf(OUTPUT,CF_BUFSIZE*2,"Couldn't open lock database %s\n",name); | ||
| 487 : | CfLog(cferror,OUTPUT,"db_open"); | ||
| 488 : | return NULL; | ||
| 489 : | } | ||
| 490 : | |||
| 491 : | return dbp; | ||
| 492 : | } | ||
| 493 : | |||
| 494 : | /************************************************************************/ | ||
| 495 : | |||
| 496 : | void CloseLock(DB *dbp) | ||
| 497 : | |||
| 498 : | { | ||
| 499 : | dbp->close(dbp,0); | ||
| 500 : | } |
| Administrator | ViewVC Help |
| Powered by ViewVC 1.0.3 |