1 /*
2 Copyright 2021 Northern.tech AS
3
4 This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
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; version 3.
9
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 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18
19 To the extent this program is licensed as part of the Enterprise
20 versions of CFEngine, the applicable Commercial Open Source License
21 (COSL) may apply to this file if you as a licensee so wish it. See
22 included file COSL.txt.
23 */
24
25 #include <locks.h>
26 #include <global_mutex.h>
27 #include <mutex.h>
28 #include <string_lib.h>
29 #include <files_interfaces.h>
30 #include <files_lib.h>
31 #include <cleanup.h>
32 #include <policy.h>
33 #include <hash.h>
34 #include <rb-tree.h>
35 #include <files_names.h>
36 #include <rlist.h>
37 #include <process_lib.h>
38 #include <fncall.h>
39 #include <eval_context.h>
40 #include <misc_lib.h>
41 #include <known_dirs.h>
42 #include <sysinfo.h>
43 #include <openssl/evp.h>
44 #include <libcrypto-compat.h>
45
46 #ifdef LMDB
47 // Be careful if you want to change this,
48 // it must match mdb_env_get_maxkeysize(env)
49 #define LMDB_MAX_KEY_SIZE 511
50 #endif
51
52 #define CFLOGSIZE 1048576 /* Size of lock-log before rotation */
53 #define CF_LOCKHORIZON ((time_t)(SECONDS_PER_WEEK * 4))
54 #define CF_MAXLOCKNUM 8192
55
56 #define CF_CRITIAL_SECTION "CF_CRITICAL_SECTION"
57
58 #define LOG_LOCK_ENTRY(__lock, __lock_sum, __lock_data) \
59 log_lock("Entering", __FUNCTION__, __lock, __lock_sum, __lock_data)
60 #define LOG_LOCK_EXIT(__lock, __lock_sum, __lock_data) \
61 log_lock("Exiting", __FUNCTION__, __lock, __lock_sum, __lock_data)
62 #define LOG_LOCK_OP(__lock, __lock_sum, __lock_data) \
63 log_lock("Performing", __FUNCTION__, __lock, __lock_sum, __lock_data)
64
65 typedef struct CfLockStack_ {
66 char lock[CF_BUFSIZE];
67 char last[CF_BUFSIZE];
68 struct CfLockStack_ *previous;
69 } CfLockStack;
70
71 static CfLockStack *LOCK_STACK = NULL;
72
PushLock(char * lock,char * last)73 static void PushLock(char *lock, char *last)
74 {
75 CfLockStack *new_lock = malloc(sizeof(CfLockStack));
76 strlcpy(new_lock->lock, lock, CF_BUFSIZE);
77 strlcpy(new_lock->last, last, CF_BUFSIZE);
78
79 new_lock->previous = LOCK_STACK;
80 LOCK_STACK = new_lock;
81 }
82
PopLock()83 static CfLockStack *PopLock()
84 {
85 if (!LOCK_STACK)
86 {
87 return NULL;
88 }
89 CfLockStack *lock = LOCK_STACK;
90 LOCK_STACK = lock->previous;
91 return lock;
92 }
93
94 static void CopyLockDatabaseAtomically(const char *from, const char *to,
95 const char *from_pretty_name,
96 const char *to_pretty_name);
97
98 static void RestoreLockDatabase(void);
99
VerifyThatDatabaseIsNotCorrupt_once(void)100 static void VerifyThatDatabaseIsNotCorrupt_once(void)
101 {
102 int uptime = GetUptimeSeconds(time(NULL));
103 if (uptime <= 0)
104 {
105 Log(LOG_LEVEL_VERBOSE,
106 "Not able to determine uptime when verifying lock database. "
107 "Will assume the database is in order.");
108 return;
109 }
110
111 char *db_path = DBIdToPath(dbid_locks);
112 struct stat statbuf;
113 if (stat(db_path, &statbuf) == 0)
114 {
115 if (statbuf.st_mtime < time(NULL) - uptime)
116 {
117 // We have rebooted since the database was last updated.
118 // Restore it from our backup.
119 RestoreLockDatabase();
120 }
121 }
122
123 free(db_path);
124 }
125
VerifyThatDatabaseIsNotCorrupt(void)126 static void VerifyThatDatabaseIsNotCorrupt(void)
127 {
128 static pthread_once_t uptime_verified = PTHREAD_ONCE_INIT;
129 pthread_once(&uptime_verified, &VerifyThatDatabaseIsNotCorrupt_once);
130 }
131
OpenLock()132 CF_DB *OpenLock()
133 {
134 CF_DB *dbp;
135
136 VerifyThatDatabaseIsNotCorrupt();
137
138 if (!OpenDB(&dbp, dbid_locks))
139 {
140 return NULL;
141 }
142
143 return dbp;
144 }
145
CloseLock(CF_DB * dbp)146 void CloseLock(CF_DB *dbp)
147 {
148 if (dbp)
149 {
150 CloseDB(dbp);
151 }
152 }
153
154 static pthread_once_t lock_cleanup_once = PTHREAD_ONCE_INIT; /* GLOBAL_X */
155
156 #ifdef LMDB
log_lock(const char * op,const char * function,const char * lock,const char * lock_sum,const LockData * lock_data)157 static inline void log_lock(const char *op,
158 const char *function,
159 const char *lock,
160 const char *lock_sum,
161 const LockData *lock_data)
162 {
163 /* Check log level first to save cycles when not in debug mode. */
164 if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG)
165 {
166 if (lock_data)
167 {
168 LogDebug(LOG_MOD_LOCKS, "%s lock operation in '%s()': "
169 "lock_id = '%s', lock_checksum = '%s', "
170 "lock.pid = '%d', lock.time = '%d', "
171 "lock.process_start_time = '%d'",
172 op, function, lock, lock_sum,
173 (int)lock_data->pid, (int)lock_data->time,
174 (int)lock_data->process_start_time);
175 }
176 else
177 {
178 LogDebug(LOG_MOD_LOCKS, "%s lock operation in '%s()'. "
179 "lock_id = '%s', lock_checksum = '%s'",
180 op, function, lock, lock_sum);
181 }
182 }
183 }
184
HashLockKeyIfNecessary(const char * const istring,char * const ohash)185 static void HashLockKeyIfNecessary(const char *const istring, char *const ohash)
186 {
187 assert(strlen("CF_CRITICAL_SECTION") < LMDB_MAX_KEY_SIZE);
188 assert(strlen("lock.track_license_bundle.track_license") < LMDB_MAX_KEY_SIZE);
189 StringCopyTruncateAndHashIfNecessary(istring, ohash, LMDB_MAX_KEY_SIZE);
190 }
191 #endif
192
WriteLockData(CF_DB * dbp,const char * lock_id,LockData * lock_data)193 static bool WriteLockData(CF_DB *dbp, const char *lock_id, LockData *lock_data)
194 {
195 bool ret;
196
197 #ifdef LMDB
198 unsigned char digest2[LMDB_MAX_KEY_SIZE];
199
200 HashLockKeyIfNecessary(lock_id, digest2);
201
202 LOG_LOCK_ENTRY(lock_id, digest2, lock_data);
203 ret = WriteDB(dbp, digest2, lock_data, sizeof(LockData));
204 LOG_LOCK_EXIT(lock_id, digest2, lock_data);
205 #else
206 ret = WriteDB(dbp, lock_id, lock_data, sizeof(LockData));
207 #endif
208
209 return ret;
210 }
211
WriteLockDataCurrent(CF_DB * dbp,const char * lock_id)212 static bool WriteLockDataCurrent(CF_DB *dbp, const char *lock_id)
213 {
214 LockData lock_data = { 0 };
215 lock_data.pid = getpid();
216 lock_data.time = time(NULL);
217 lock_data.process_start_time = GetProcessStartTime(getpid());
218
219 return WriteLockData(dbp, lock_id, &lock_data);
220 }
221
WriteLock(const char * name)222 static int WriteLock(const char *name)
223 {
224 CF_DB *dbp = OpenLock();
225
226 if (dbp == NULL)
227 {
228 return -1;
229 }
230
231 ThreadLock(cft_lock);
232 WriteLockDataCurrent(dbp, name);
233
234 CloseLock(dbp);
235 ThreadUnlock(cft_lock);
236
237 return 0;
238 }
239
FindLockTime(const char * name)240 static time_t FindLockTime(const char *name)
241 {
242 bool ret;
243 CF_DB *dbp = OpenLock();
244 if (dbp == NULL)
245 {
246 return -1;
247 }
248
249 LockData entry = { 0 };
250 entry.process_start_time = PROCESS_START_TIME_UNKNOWN;
251
252 #ifdef LMDB
253 unsigned char ohash[LMDB_MAX_KEY_SIZE];
254 HashLockKeyIfNecessary(name, ohash);
255
256 LOG_LOCK_ENTRY(name, ohash, &entry);
257 ret = ReadDB(dbp, ohash, &entry, sizeof(entry));
258 LOG_LOCK_EXIT(name, ohash, &entry);
259 #else
260 ret = ReadDB(dbp, name, &entry, sizeof(entry));
261 #endif
262
263 if (ret)
264 {
265 CloseLock(dbp);
266 return entry.time;
267 }
268 else
269 {
270 CloseLock(dbp);
271 return -1;
272 }
273 }
274
RemoveDates(char * s)275 static void RemoveDates(char *s)
276 {
277 int i, a = 0, b = 0, c = 0, d = 0;
278 char *dayp = NULL, *monthp = NULL, *sp;
279 char *days[7] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
280 char *months[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
281 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
282
283 // Canonifies or blanks our times/dates for locks where there would be an explosion of state
284
285 /* Has s always been generated by something that uses two-digit hh:mm:ss?
286 * Are there any time-zones whose abbreviations are shorter than three
287 * letters?
288 */
289 if (strlen(s) < sizeof("Fri Oct 1 15:15:23 EST 2010") - 1)
290 {
291 // Probably not a full date
292 return;
293 }
294
295 for (i = 0; i < 7; i++)
296 {
297 if ((dayp = strstr(s, days[i])))
298 {
299 *dayp = 'D';
300 *(dayp + 1) = 'A';
301 *(dayp + 2) = 'Y';
302 break;
303 }
304 }
305
306 for (i = 0; i < 12; i++)
307 {
308 if ((monthp = strstr(s, months[i])))
309 {
310 *monthp = 'M';
311 *(monthp + 1) = 'O';
312 *(monthp + 2) = 'N';
313 break;
314 }
315 }
316
317 if (dayp && monthp) // looks like a full date
318 {
319 sscanf(monthp + 4, "%d %d:%d:%d", &a, &b, &c, &d);
320
321 if (a * b * c * d == 0)
322 {
323 // Probably not a date
324 return;
325 }
326
327 for (sp = monthp + 4; *sp != '\0'; sp++)
328 {
329 if (sp > monthp + 15)
330 {
331 break;
332 }
333
334 if (isdigit((int)*sp))
335 {
336 *sp = 't';
337 }
338 }
339 }
340 }
341
RemoveLock(const char * name)342 static int RemoveLock(const char *name)
343 {
344 CF_DB *dbp = OpenLock();
345 if (dbp == NULL)
346 {
347 return -1;
348 }
349
350 ThreadLock(cft_lock);
351 #ifdef LMDB
352 unsigned char digest2[LMDB_MAX_KEY_SIZE];
353
354 HashLockKeyIfNecessary(name, digest2);
355
356 LOG_LOCK_ENTRY(name, digest2, NULL);
357 DeleteDB(dbp, digest2);
358 LOG_LOCK_EXIT(name, digest2, NULL);
359 #else
360 DeleteDB(dbp, name);
361 #endif
362 ThreadUnlock(cft_lock);
363
364 CloseLock(dbp);
365 return 0;
366 }
367
NoOrObsoleteLock(LockData * entry,ARG_UNUSED size_t entry_size,size_t * max_old)368 static bool NoOrObsoleteLock(LockData *entry, ARG_UNUSED size_t entry_size, size_t *max_old)
369 {
370 assert((entry == NULL) || (entry_size == sizeof(LockData)));
371
372 if (entry == NULL)
373 {
374 return true;
375 }
376
377 time_t now = time(NULL);
378 if ((now - entry->time) <= (time_t) *max_old)
379 {
380 Log(LOG_LEVEL_DEBUG, "Giving time to process '%d' (holding lock for %ld s)", entry->pid, (now - entry->time));
381 }
382 return ((now - entry->time) > (time_t) *max_old);
383 }
384
WaitForCriticalSection(const char * section_id)385 void WaitForCriticalSection(const char *section_id)
386 {
387 ThreadLock(cft_lock);
388
389 CF_DB *dbp = OpenLock();
390 if (dbp == NULL)
391 {
392 Log(LOG_LEVEL_CRIT, "Failed to open lock database when waiting for critical section");
393 ThreadUnlock(cft_lock);
394 return;
395 }
396
397 time_t started = time(NULL);
398 LockData entry = { 0 };
399 entry.pid = getpid();
400 entry.process_start_time = PROCESS_START_TIME_UNKNOWN;
401
402 #ifdef LMDB
403 unsigned char ohash[LMDB_MAX_KEY_SIZE];
404 HashLockKeyIfNecessary(section_id, ohash);
405 Log(LOG_LEVEL_DEBUG, "Hashed critical section lock '%s' to '%s'", section_id, ohash);
406 section_id = ohash;
407 #endif
408
409 /* If another agent has been waiting more than a minute, it means there
410 is likely crash detritus to clear up... After a minute we take our
411 chances ... */
412 size_t max_old = 60;
413
414 Log(LOG_LEVEL_DEBUG, "Acquiring critical section lock '%s'", section_id);
415 bool got_lock = false;
416 while (!got_lock && ((time(NULL) - started) <= (time_t) max_old))
417 {
418 entry.time = time(NULL);
419 got_lock = OverwriteDB(dbp, section_id, &entry, sizeof(entry),
420 (OverwriteCondition) NoOrObsoleteLock, &max_old);
421 if (!got_lock)
422 {
423 Log(LOG_LEVEL_DEBUG, "Waiting for critical section lock '%s'", section_id);
424 sleep(1);
425 }
426 }
427
428 /* If we still haven't gotten the lock, let's try the biggest hammer we
429 * have. */
430 if (!got_lock)
431 {
432 Log(LOG_LEVEL_NOTICE, "Failed to wait for critical section lock '%s', force-writing new lock", section_id);
433 if (!WriteDB(dbp, section_id, &entry, sizeof(entry)))
434 {
435 Log(LOG_LEVEL_CRIT, "Failed to force-write critical section lock '%s'", section_id);
436 }
437 }
438 else
439 {
440 Log(LOG_LEVEL_DEBUG, "Acquired critical section lock '%s'", section_id);
441 }
442
443 CloseLock(dbp);
444 ThreadUnlock(cft_lock);
445 }
446
ReleaseCriticalSection(const char * section_id)447 void ReleaseCriticalSection(const char *section_id)
448 {
449 Log(LOG_LEVEL_DEBUG, "Releasing critical section lock '%s'", section_id);
450 if (RemoveLock(section_id) == 0)
451 {
452 Log(LOG_LEVEL_DEBUG, "Released critical section lock '%s'", section_id);
453 }
454 else
455 {
456 Log(LOG_LEVEL_DEBUG, "Failed to release critical section lock '%s'", section_id);
457 }
458 }
459
FindLock(char * last)460 static time_t FindLock(char *last)
461 {
462 time_t mtime;
463
464 if ((mtime = FindLockTime(last)) == -1)
465 {
466 /* Do this to prevent deadlock loops from surviving if IfElapsed > T_sched */
467
468 if (WriteLock(last) == -1)
469 {
470 Log(LOG_LEVEL_ERR, "Unable to lock %s", last);
471 return 0;
472 }
473
474 return 0;
475 }
476 else
477 {
478 return mtime;
479 }
480 }
481
LocksCleanup(void)482 static void LocksCleanup(void)
483 {
484 CfLockStack *lock;
485 while ((lock = PopLock()) != NULL)
486 {
487 CfLock best_guess = {
488 .lock = xstrdup(lock->lock),
489 .last = xstrdup(lock->last),
490 };
491 YieldCurrentLock(best_guess);
492 free(lock);
493 }
494 }
495
RegisterLockCleanup(void)496 static void RegisterLockCleanup(void)
497 {
498 RegisterCleanupFunction(&LocksCleanup);
499 }
500
501 /**
502 * Return a type template for the promise for lock-type
503 * identification. WARNING: instead of truncation, it does not include any
504 * parts (i.e. constraints or promise type) that don't fit.
505 *
506 * @WARNING currently it only prints up to the 5 first constraints (WHY?)
507 */
PromiseTypeString(char * dst,size_t dst_size,const Promise * pp)508 static void PromiseTypeString(char *dst, size_t dst_size, const Promise *pp)
509 {
510 const char *sp = PromiseGetPromiseType(pp);
511 size_t sp_len = strlen(sp);
512
513 dst[0] = '\0';
514 size_t dst_len = 0;
515
516 if (sp_len + 1 < dst_size)
517 {
518 strcpy(dst, sp);
519 strcat(dst, ".");
520 dst_len += sp_len + 1;
521 }
522
523 if (pp->conlist != NULL)
524 {
525 /* Number of constraints (attributes) of that promise. */
526 size_t cons_num = SeqLength(pp->conlist);
527 for (size_t i = 0; (i < 5) && (i < cons_num); i++)
528 {
529 Constraint *cp = SeqAt(pp->conlist, i);
530 const char *con = cp->lval; /* the constraint */
531
532 /* Exception for args (promise type commands),
533 by symmetry, for locking. */
534 if (strcmp(con, "args") == 0)
535 {
536 continue;
537 }
538
539 /* Exception for arglist (promise type commands),
540 by symmetry, for locking. */
541 if (strcmp(con, "arglist") == 0)
542 {
543 continue;
544 }
545
546 size_t con_len = strlen(con);
547 if (dst_len + con_len + 1 < dst_size)
548 {
549 strcat(dst, con);
550 strcat(dst, ".");
551 dst_len += con_len + 1;
552 }
553 }
554 }
555 }
556
KillLockHolder(const char * lock)557 static bool KillLockHolder(const char *lock)
558 {
559 bool ret;
560 CF_DB *dbp = OpenLock();
561 if (dbp == NULL)
562 {
563 Log(LOG_LEVEL_ERR, "Unable to open locks database");
564 return false;
565 }
566
567 LockData lock_data = { 0 };
568 lock_data.process_start_time = PROCESS_START_TIME_UNKNOWN;
569
570 #ifdef LMDB
571 unsigned char ohash[LMDB_MAX_KEY_SIZE];
572 HashLockKeyIfNecessary(lock, ohash);
573
574 LOG_LOCK_ENTRY(lock, ohash, &lock_data);
575 ret = ReadDB(dbp, ohash, &lock_data, sizeof(lock_data));
576 LOG_LOCK_EXIT(lock, ohash, &lock_data);
577 #else
578 ret = ReadDB(dbp, lock, &lock_data, sizeof(lock_data));
579 #endif
580
581 if (!ret)
582 {
583 /* No lock found */
584 CloseLock(dbp);
585 return true;
586 }
587
588 CloseLock(dbp);
589
590 if (GracefulTerminate(lock_data.pid, lock_data.process_start_time))
591 {
592 Log(LOG_LEVEL_INFO,
593 "Process with PID %jd successfully killed",
594 (intmax_t) lock_data.pid);
595 return true;
596 }
597 else
598 {
599 if (errno == ESRCH)
600 {
601 Log(LOG_LEVEL_VERBOSE,
602 "Process with PID %jd has already been killed",
603 (intmax_t) lock_data.pid);
604 return true;
605 }
606 else
607 {
608 Log(LOG_LEVEL_ERR,
609 "Failed to kill process with PID: %jd (kill: %s)",
610 (intmax_t) lock_data.pid, GetErrorStr());
611 return false;
612 }
613 }
614 }
615
RvalDigestUpdate(EVP_MD_CTX * context,Rlist * rp)616 static void RvalDigestUpdate(EVP_MD_CTX *context, Rlist *rp)
617 {
618 assert(context != NULL);
619 assert(rp != NULL);
620
621 switch (rp->val.type)
622 {
623 case RVAL_TYPE_SCALAR:
624 EVP_DigestUpdate(context, RlistScalarValue(rp),
625 strlen(RlistScalarValue(rp)));
626 break;
627
628 case RVAL_TYPE_FNCALL:
629 // TODO: This should be recursive and not just hash the function name
630 EVP_DigestUpdate(context, RlistFnCallValue(rp)->name,
631 strlen(RlistFnCallValue(rp)->name));
632 break;
633
634 default:
635 ProgrammingError("Unhandled case in switch");
636 break;
637 }
638 }
639
PromiseRuntimeHash(const Promise * pp,const char * salt,unsigned char digest[EVP_MAX_MD_SIZE+1],HashMethod type)640 void PromiseRuntimeHash(const Promise *pp, const char *salt,
641 unsigned char digest[EVP_MAX_MD_SIZE + 1],
642 HashMethod type)
643 {
644 static const char PACK_UPIFELAPSED_SALT[] = "packageuplist";
645
646 int md_len;
647 const EVP_MD *md = NULL;
648 Rlist *rp;
649 FnCall *fp;
650
651 char *noRvalHash[] = { "mtime", "atime", "ctime", "stime_range", "ttime_range", "log_string", NULL };
652 int doHash;
653
654 md = HashDigestFromId(type);
655 if (md == NULL)
656 {
657 Log(LOG_LEVEL_ERR,
658 "Could not determine function for file hashing (type=%d)",
659 (int) type);
660 return;
661 }
662
663 EVP_MD_CTX *context = EVP_MD_CTX_new();
664 if (context == NULL)
665 {
666 Log(LOG_LEVEL_ERR, "Could not allocate openssl hash context");
667 return;
668 }
669
670 EVP_DigestInit(context, md);
671
672 // multiple packages (promisers) may share same package_list_update_ifelapsed lock
673 if ( (!salt) || strcmp(salt, PACK_UPIFELAPSED_SALT) )
674 {
675 EVP_DigestUpdate(context, pp->promiser, strlen(pp->promiser));
676 }
677
678 if (pp->comment)
679 {
680 EVP_DigestUpdate(context, pp->comment, strlen(pp->comment));
681 }
682
683 if (pp->parent_section && pp->parent_section->parent_bundle)
684 {
685 if (pp->parent_section->parent_bundle->ns)
686 {
687 EVP_DigestUpdate(context,
688 pp->parent_section->parent_bundle->ns,
689 strlen(pp->parent_section->parent_bundle->ns));
690 }
691
692 if (pp->parent_section->parent_bundle->name)
693 {
694 EVP_DigestUpdate(context,
695 pp->parent_section->parent_bundle->name,
696 strlen(pp->parent_section->parent_bundle->name));
697 }
698 }
699
700 // Unused: pp start, end, and line attributes (describing source position).
701
702 if (salt)
703 {
704 EVP_DigestUpdate(context, salt, strlen(salt));
705 }
706
707 if (pp->conlist)
708 {
709 for (size_t i = 0; i < SeqLength(pp->conlist); i++)
710 {
711 Constraint *cp = SeqAt(pp->conlist, i);
712
713 EVP_DigestUpdate(context, cp->lval, strlen(cp->lval));
714
715 // don't hash rvals that change (e.g. times)
716 doHash = true;
717
718 for (int j = 0; noRvalHash[j] != NULL; j++)
719 {
720 if (strcmp(cp->lval, noRvalHash[j]) == 0)
721 {
722 doHash = false;
723 break;
724 }
725 }
726
727 if (!doHash)
728 {
729 continue;
730 }
731
732 switch (cp->rval.type)
733 {
734 case RVAL_TYPE_SCALAR:
735 EVP_DigestUpdate(context, cp->rval.item, strlen(cp->rval.item));
736 break;
737
738 case RVAL_TYPE_LIST:
739 for (rp = cp->rval.item; rp != NULL; rp = rp->next)
740 {
741 RvalDigestUpdate(context, rp);
742 }
743 break;
744
745 case RVAL_TYPE_FNCALL:
746
747 /* Body or bundle */
748
749 fp = (FnCall *) cp->rval.item;
750
751 EVP_DigestUpdate(context, fp->name, strlen(fp->name));
752
753 for (rp = fp->args; rp != NULL; rp = rp->next)
754 {
755 RvalDigestUpdate(context, rp);
756 }
757 break;
758
759 default:
760 break;
761 }
762 }
763 }
764
765 EVP_DigestFinal(context, digest, &md_len);
766 EVP_MD_CTX_free(context);
767
768 /* Digest length stored in md_len */
769 }
770
CfLockNew(const char * last,const char * lock,bool is_dummy)771 static CfLock CfLockNew(const char *last, const char *lock, bool is_dummy)
772 {
773 return (CfLock) {
774 .last = last ? xstrdup(last) : NULL,
775 .lock = lock ? xstrdup(lock) : NULL,
776 .is_dummy = is_dummy
777 };
778 }
779
CfLockNull(void)780 static CfLock CfLockNull(void)
781 {
782 return (CfLock) {
783 .last = NULL,
784 .lock = NULL,
785 .is_dummy = false
786 };
787 }
788
AcquireLock(EvalContext * ctx,const char * operand,const char * host,time_t now,int ifelapsed,int expireafter,const Promise * pp,bool ignoreProcesses)789 CfLock AcquireLock(EvalContext *ctx, const char *operand, const char *host,
790 time_t now, int ifelapsed, int expireafter, const Promise *pp,
791 bool ignoreProcesses)
792 {
793 if (now == 0)
794 {
795 return CfLockNull();
796 }
797
798 char str_digest[CF_HOSTKEY_STRING_SIZE];
799 {
800 unsigned char digest[EVP_MAX_MD_SIZE + 1];
801 PromiseRuntimeHash(pp, operand, digest, CF_DEFAULT_DIGEST);
802 HashPrintSafe(str_digest, sizeof(str_digest), digest,
803 CF_DEFAULT_DIGEST, true);
804 }
805
806 if (EvalContextPromiseLockCacheContains(ctx, str_digest))
807 {
808 // Log(LOG_LEVEL_DEBUG, "This promise has already been verified");
809 return CfLockNull();
810 }
811
812 EvalContextPromiseLockCachePut(ctx, str_digest);
813
814 // Finally if we're supposed to ignore locks ... do the remaining stuff
815 if (EvalContextIsIgnoringLocks(ctx))
816 {
817 return CfLockNew(NULL, "dummy", true);
818 }
819
820 char cc_operator[CF_MAXVARSIZE];
821 {
822 char promise[CF_MAXVARSIZE - CF_BUFFERMARGIN];
823 PromiseTypeString(promise, sizeof(promise), pp);
824 snprintf(cc_operator, sizeof(cc_operator), "%s-%s", promise, host);
825 }
826
827 char cc_operand[CF_BUFSIZE];
828 strlcpy(cc_operand, operand, CF_BUFSIZE);
829 CanonifyNameInPlace(cc_operand);
830 RemoveDates(cc_operand);
831
832
833 Log(LOG_LEVEL_DEBUG,
834 "AcquireLock(%s,%s), ExpireAfter = %d, IfElapsed = %d",
835 cc_operator, cc_operand, expireafter, ifelapsed);
836
837 int sum = 0;
838 for (int i = 0; cc_operator[i] != '\0'; i++)
839 {
840 sum = (CF_MACROALPHABET * sum + cc_operator[i]) % CF_MAXLOCKNUM;
841 }
842
843 for (int i = 0; cc_operand[i] != '\0'; i++)
844 {
845 sum = (CF_MACROALPHABET * sum + cc_operand[i]) % CF_MAXLOCKNUM;
846 }
847
848 const char *bundle_name = PromiseGetBundle(pp)->name;
849
850 char cflock[CF_BUFSIZE] = "";
851 snprintf(cflock, CF_BUFSIZE, "lock.%.100s.%s.%.100s_%d_%s",
852 bundle_name, cc_operator, cc_operand, sum, str_digest);
853
854 char cflast[CF_BUFSIZE] = "";
855 snprintf(cflast, CF_BUFSIZE, "last.%.100s.%s.%.100s_%d_%s",
856 bundle_name, cc_operator, cc_operand, sum, str_digest);
857
858 Log(LOG_LEVEL_DEBUG, "Locking bundle '%s' with lock '%s'",
859 bundle_name, cflock);
860
861 // Now see if we can get exclusivity to edit the locks
862 WaitForCriticalSection(CF_CRITIAL_SECTION);
863
864 // Look for non-existent (old) processes
865 time_t lastcompleted = FindLock(cflast);
866 time_t elapsedtime = (time_t) (now - lastcompleted) / 60;
867
868 // For promises/locks with ifelapsed == 0, skip all detection logic of
869 // previously acquired locks, whether in this agent or a parallel one.
870 if (ifelapsed != 0)
871 {
872 if (elapsedtime < 0)
873 {
874 Log(LOG_LEVEL_VERBOSE,
875 "Another cf-agent seems to have done this since I started "
876 "(elapsed=%jd)",
877 (intmax_t) elapsedtime);
878 ReleaseCriticalSection(CF_CRITIAL_SECTION);
879 return CfLockNull();
880 }
881
882 if (elapsedtime < ifelapsed)
883 {
884 Log(LOG_LEVEL_VERBOSE,
885 "Nothing promised here [%.40s] (%jd/%u minutes elapsed)",
886 cflast, (intmax_t) elapsedtime, ifelapsed);
887 ReleaseCriticalSection(CF_CRITIAL_SECTION);
888 return CfLockNull();
889 }
890 }
891
892 // Look for existing (current) processes
893 lastcompleted = FindLock(cflock);
894 if (!ignoreProcesses)
895 {
896 elapsedtime = (time_t) (now - lastcompleted) / 60;
897
898 if (lastcompleted != 0)
899 {
900 if (elapsedtime >= expireafter)
901 {
902 Log(LOG_LEVEL_INFO,
903 "Lock expired after %jd/%u minutes: %s",
904 (intmax_t) elapsedtime, expireafter, cflock);
905
906 if (KillLockHolder(cflock))
907 {
908 Log(LOG_LEVEL_VERBOSE,
909 "Lock successfully expired: %s", cflock);
910 unlink(cflock);
911 }
912 else
913 {
914 Log(LOG_LEVEL_ERR, "Failed to expire lock: %s", cflock);
915 }
916 }
917 else
918 {
919 ReleaseCriticalSection(CF_CRITIAL_SECTION);
920 Log(LOG_LEVEL_VERBOSE,
921 "Couldn't obtain lock for %s (already running!)",
922 cflock);
923 return CfLockNull();
924 }
925 }
926
927 int ret = WriteLock(cflock);
928 if (ret != -1)
929 {
930 /* Register a cleanup handler *after* having opened the DB, so that
931 * CloseAllDB() atexit() handler is registered in advance, and it
932 * is called after removing this lock.
933
934 * There is a small race condition here that we'll leave a stale
935 * lock if we exit before the following line. */
936 pthread_once(&lock_cleanup_once, &RegisterLockCleanup);
937 }
938 }
939
940 ReleaseCriticalSection(CF_CRITIAL_SECTION);
941
942 // Keep this as a global for signal handling
943 PushLock(cflock, cflast);
944
945 return CfLockNew(cflast, cflock, false);
946 }
947
YieldCurrentLock(CfLock lock)948 void YieldCurrentLock(CfLock lock)
949 {
950 if (lock.is_dummy)
951 {
952 free(lock.lock); /* allocated in AquireLock as a special case */
953 return;
954 }
955
956 if (lock.lock == (char *) CF_UNDEFINED)
957 {
958 return;
959 }
960
961 Log(LOG_LEVEL_DEBUG, "Yielding lock '%s'", lock.lock);
962
963 if (RemoveLock(lock.lock) == -1)
964 {
965 Log(LOG_LEVEL_VERBOSE, "Unable to remove lock %s", lock.lock);
966 free(lock.last);
967 free(lock.lock);
968 return;
969 }
970
971 if (WriteLock(lock.last) == -1)
972 {
973 Log(LOG_LEVEL_ERR, "Unable to create '%s'. (create: %s)",
974 lock.last, GetErrorStr());
975 free(lock.last);
976 free(lock.lock);
977 return;
978 }
979
980 /* This lock has ben yield'ed, don't try to yield it again in case process
981 * is terminated abnormally.
982 */
983 CfLockStack *stack = LOCK_STACK;
984 CfLockStack *last = NULL;
985 while (stack)
986 {
987 if ((strcmp(stack->lock, lock.lock) == 0)
988 && (strcmp(stack->last, lock.last) == 0))
989 {
990 CfLockStack *delete_me = stack;
991 stack = stack->previous;
992 if (!last)
993 {
994 assert(delete_me == LOCK_STACK);
995 LOCK_STACK = stack;
996 } else {
997 last->previous = stack;
998 }
999 free(delete_me);
1000 continue;
1001 }
1002 last = stack;
1003 stack = stack->previous;
1004 }
1005
1006 free(lock.last);
1007 free(lock.lock);
1008 }
1009
YieldCurrentLockAndRemoveFromCache(EvalContext * ctx,CfLock lock,const char * operand,const Promise * pp)1010 void YieldCurrentLockAndRemoveFromCache(EvalContext *ctx, CfLock lock,
1011 const char *operand, const Promise *pp)
1012 {
1013 unsigned char digest[EVP_MAX_MD_SIZE + 1];
1014 PromiseRuntimeHash(pp, operand, digest, CF_DEFAULT_DIGEST);
1015 char str_digest[CF_HOSTKEY_STRING_SIZE];
1016 HashPrintSafe(str_digest, sizeof(str_digest), digest,
1017 CF_DEFAULT_DIGEST, true);
1018
1019 YieldCurrentLock(lock);
1020 EvalContextPromiseLockCacheRemove(ctx, str_digest);
1021 }
1022
1023
GetLockName(char * lockname,const char * locktype,const char * base,const Rlist * params)1024 void GetLockName(char *lockname, const char *locktype,
1025 const char *base, const Rlist *params)
1026 {
1027 int max_sample, count = 0;
1028
1029 for (const Rlist *rp = params; rp != NULL; rp = rp->next)
1030 {
1031 count++;
1032 }
1033
1034 if (count)
1035 {
1036 max_sample = CF_BUFSIZE / (2 * count);
1037 }
1038 else
1039 {
1040 max_sample = 0;
1041 }
1042
1043 strlcpy(lockname, locktype, CF_BUFSIZE / 10);
1044 strlcat(lockname, "_", CF_BUFSIZE / 10);
1045 strlcat(lockname, base, CF_BUFSIZE / 10);
1046 strlcat(lockname, "_", CF_BUFSIZE / 10);
1047
1048 for (const Rlist *rp = params; rp != NULL; rp = rp->next)
1049 {
1050 switch (rp->val.type)
1051 {
1052 case RVAL_TYPE_SCALAR:
1053 strncat(lockname, RlistScalarValue(rp), max_sample);
1054 break;
1055
1056 case RVAL_TYPE_FNCALL:
1057 strncat(lockname, RlistFnCallValue(rp)->name, max_sample);
1058 break;
1059
1060 default:
1061 ProgrammingError("Unhandled case in switch %d", rp->val.type);
1062 break;
1063 }
1064 }
1065 }
1066
RestoreLockDatabase(void)1067 static void RestoreLockDatabase(void)
1068 {
1069 // We don't do any locking here (since we can't trust the database), but
1070 // this should be right after bootup, so we should be the only one.
1071 // Worst case someone else will just copy the same file to the same
1072 // location.
1073 char *db_path = DBIdToPath(dbid_locks);
1074 char *db_path_backup;
1075 xasprintf(&db_path_backup, "%s.backup", db_path);
1076
1077 CopyLockDatabaseAtomically(db_path_backup, db_path, "lock database backup",
1078 "lock database");
1079
1080 free(db_path);
1081 free(db_path_backup);
1082 }
1083
CopyLockDatabaseAtomically(const char * from,const char * to,const char * from_pretty_name,const char * to_pretty_name)1084 static void CopyLockDatabaseAtomically(const char *from, const char *to,
1085 const char *from_pretty_name,
1086 const char *to_pretty_name)
1087 {
1088 char *tmp_file_name;
1089 xasprintf(&tmp_file_name, "%s.tmp", to);
1090
1091 int from_fd = open(from, O_RDONLY | O_BINARY);
1092 if (from_fd < 0)
1093 {
1094 Log(LOG_LEVEL_WARNING,
1095 "Could not open '%s' (open: %s)",
1096 from_pretty_name, GetErrorStr());
1097 goto cleanup;
1098 }
1099
1100 int to_fd = open(tmp_file_name, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600);
1101 if (to_fd < 0)
1102 {
1103 Log(LOG_LEVEL_WARNING,
1104 "Could not open '%s' temporary file (open: %s)",
1105 to_pretty_name, GetErrorStr());
1106 goto cleanup;
1107 }
1108
1109 size_t total_bytes_written;
1110 bool last_write_was_hole;
1111 bool ok1 = FileSparseCopy(from_fd, from_pretty_name,
1112 to_fd, to_pretty_name, DEV_BSIZE,
1113 &total_bytes_written, &last_write_was_hole);
1114
1115 /* Make sure changes are persistent on disk, so database cannot get
1116 * corrupted at system crash. */
1117 bool do_sync = true;
1118 bool ok2 = FileSparseClose(to_fd, to_pretty_name, do_sync,
1119 total_bytes_written, last_write_was_hole);
1120
1121 if (!ok1 || !ok2)
1122 {
1123 Log(LOG_LEVEL_WARNING,
1124 "Error while moving database from '%s' to '%s'",
1125 from_pretty_name, to_pretty_name);
1126 }
1127
1128 if (rename(tmp_file_name, to) != 0)
1129 {
1130 Log(LOG_LEVEL_WARNING,
1131 "Could not move '%s' into place (rename: %s)",
1132 to_pretty_name, GetErrorStr());
1133 }
1134
1135 cleanup:
1136 if (from_fd != -1)
1137 {
1138 close(from_fd);
1139 }
1140 unlink(tmp_file_name);
1141 free(tmp_file_name);
1142 }
1143
BackupLockDatabase(void)1144 void BackupLockDatabase(void)
1145 {
1146 WaitForCriticalSection(CF_CRITIAL_SECTION);
1147
1148 char *db_path = DBIdToPath(dbid_locks);
1149 char *db_path_backup;
1150 xasprintf(&db_path_backup, "%s.backup", db_path);
1151
1152 CopyLockDatabaseAtomically(db_path, db_path_backup, "lock database",
1153 "lock database backup");
1154
1155 free(db_path);
1156 free(db_path_backup);
1157
1158 ReleaseCriticalSection(CF_CRITIAL_SECTION);
1159 }
1160
PurgeLocks(void)1161 void PurgeLocks(void)
1162 {
1163 CF_DBC *dbcp;
1164 char *key;
1165 int ksize, vsize;
1166 LockData lock_horizon;
1167 LockData *entry = NULL;
1168 time_t now = time(NULL);
1169
1170 CF_DB *dbp = OpenLock();
1171 if (dbp == NULL)
1172 {
1173 return;
1174 }
1175
1176 memset(&lock_horizon, 0, sizeof(lock_horizon));
1177
1178 if (ReadDB(dbp, "lock_horizon", &lock_horizon, sizeof(lock_horizon)))
1179 {
1180 if (now - lock_horizon.time < SECONDS_PER_WEEK * 4)
1181 {
1182 Log(LOG_LEVEL_VERBOSE, "No lock purging scheduled");
1183 CloseLock(dbp);
1184 return;
1185 }
1186 }
1187
1188 Log(LOG_LEVEL_VERBOSE, "Looking for stale locks to purge");
1189
1190 if (!NewDBCursor(dbp, &dbcp))
1191 {
1192 char *db_path = DBIdToPath(dbid_locks);
1193 Log(LOG_LEVEL_ERR, "Unable to get cursor for locks database '%s'",
1194 db_path);
1195 free(db_path);
1196 CloseLock(dbp);
1197 return;
1198 }
1199
1200 while (NextDB(dbcp, &key, &ksize, (void **)&entry, &vsize))
1201 {
1202 #ifdef LMDB
1203 LOG_LOCK_OP("<unknown>", key, entry);
1204 #endif
1205 if (STARTSWITH(key, "last.internal_bundle.track_license.handle"))
1206 {
1207 continue;
1208 }
1209
1210 if (now - entry->time > (time_t) CF_LOCKHORIZON)
1211 {
1212 Log(LOG_LEVEL_VERBOSE, "Purging lock (%jd s elapsed): %s",
1213 (intmax_t) (now - entry->time), key);
1214 DBCursorDeleteEntry(dbcp);
1215 }
1216 }
1217
1218 Log(LOG_LEVEL_DEBUG, "Finished purging locks");
1219
1220 lock_horizon.time = now;
1221 DeleteDBCursor(dbcp);
1222
1223 WriteDB(dbp, "lock_horizon", &lock_horizon, sizeof(lock_horizon));
1224 CloseLock(dbp);
1225 }
1226