cfengine  3.15.4
About: CFEngine is a configuration management system for configuring and maintaining Unix-like computers (using an own high level policy language). Community version.
  Fossies Dox: cfengine-3.15.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

locks.c
Go to the documentation of this file.
1 /*
2  Copyright 2019 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_ {
70 
72 
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 
84 {
85  if (!LOCK_STACK)
86  {
87  return NULL;
88  }
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 
101 {
102  int uptime = GetUptimeSeconds(time(NULL));
103  if (uptime <= 0)
104  {
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.
120  }
121  }
122 
123  free(db_path);
124 }
125 
127 {
128  static pthread_once_t uptime_verified = PTHREAD_ONCE_INIT;
129  pthread_once(&uptime_verified, &VerifyThatDatabaseIsNotCorrupt_once);
130 }
131 
133 {
134  CF_DB *dbp;
135 
137 
138  if (!OpenDB(&dbp, dbid_locks))
139  {
140  return NULL;
141  }
142 
143  return dbp;
144 }
145 
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
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. */
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 
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 
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 
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 
222 static int WriteLock(const char *name)
223 {
224  CF_DB *dbp = OpenLock();
225 
226  if (dbp == NULL)
227  {
228  return -1;
229  }
230 
232  WriteLockDataCurrent(dbp, name);
233 
234  CloseLock(dbp);
236 
237  return 0;
238 }
239 
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 };
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 
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 
342 static int RemoveLock(const char *name)
343 {
344  CF_DB *dbp = OpenLock();
345  if (dbp == NULL)
346  {
347  return -1;
348  }
349 
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
363 
364  CloseLock(dbp);
365  return 0;
366 }
367 
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) <= *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) > *max_old);
383 }
384 
385 void WaitForCriticalSection(const char *section_id)
386 {
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");
394  return;
395  }
396 
397  time_t started = time(NULL);
398  LockData entry = { 0 };
399  entry.pid = getpid();
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) <= max_old))
417  {
418  entry.time = time(NULL);
419  got_lock = OverwriteDB(dbp, section_id, &entry, sizeof(entry),
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);
445 }
446 
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 
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 
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 
496 static void RegisterLockCleanup(void)
497 {
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  */
508 static void PromiseTypeString(char *dst, size_t dst_size, const Promise *pp)
509 {
510  char *sp = pp->parent_promise_type->name;
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 
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 };
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  {
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  {
602  "Process with PID %jd has already been killed",
603  (intmax_t) lock_data.pid);
604  return true;
605  }
606  else
607  {
609  "Failed to kill process with PID: %jd (kill: %s)",
610  (intmax_t) lock_data.pid, GetErrorStr());
611  return false;
612  }
613  }
614 }
615 
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 
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  {
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 
684  {
686  {
687  EVP_DigestUpdate(context,
689  strlen(pp->parent_promise_type->parent_bundle->ns));
690  }
691 
693  {
694  EVP_DigestUpdate(context,
696  strlen(pp->parent_promise_type->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 
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 
780 static CfLock CfLockNull(void)
781 {
782  return (CfLock) {
783  .last = NULL,
784  .lock = NULL,
785  .is_dummy = false
786  };
787 }
788 
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
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 
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
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  {
875  "Another cf-agent seems to have done this since I started "
876  "(elapsed=%jd)",
877  (intmax_t) elapsedtime);
879  return CfLockNull();
880  }
881 
882  if (elapsedtime < ifelapsed)
883  {
885  "Nothing promised here [%.40s] (%jd/%u minutes elapsed)",
886  cflast, (intmax_t) elapsedtime, ifelapsed);
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  {
903  "Lock expired after %jd/%u minutes: %s",
904  (intmax_t) elapsedtime, expireafter, cflock);
905 
906  if (KillLockHolder(cflock))
907  {
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  {
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 
941 
942  // Keep this as a global for signal handling
943  PushLock(cflock, cflast);
944 
945  return CfLockNew(cflast, cflock, false);
946 }
947 
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 
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 
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 
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 
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  {
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  {
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  {
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  {
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 
1145 {
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 
1159 }
1160 
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 }
char * xstrdup(const char *str)
Definition: alloc-mini.c:56
int xasprintf(char **strp, const char *fmt,...)
Definition: alloc.c:71
#define ARG_UNUSED
Definition: cf-net.c:47
#define CF_UNDEFINED
Definition: cf3.defs.h:338
#define CF_MACROALPHABET
Definition: cf3.defs.h:58
@ RVAL_TYPE_LIST
Definition: cf3.defs.h:607
@ RVAL_TYPE_SCALAR
Definition: cf3.defs.h:606
@ RVAL_TYPE_FNCALL
Definition: cf3.defs.h:608
#define SECONDS_PER_WEEK
Definition: cf3.defs.h:73
HashMethod CF_DEFAULT_DIGEST
Definition: cf3globals.c:88
#define malloc
Definition: cf3lex.c:804
void free(void *)
void RegisterCleanupFunction(CleanupFn fn)
Definition: cleanup.c:63
bool NextDB(DBCursor *cursor, char **key, int *ksize, void **value, int *vsize)
Definition: dbm_api.c:601
bool DeleteDB(DBHandle *handle, const char *key)
Definition: dbm_api.c:583
bool DBCursorDeleteEntry(DBCursor *cursor)
Definition: dbm_api.c:607
bool OpenDB(DBHandle **dbp, dbid id)
Definition: dbm_api.c:441
char * DBIdToPath(dbid id)
Definition: dbm_api.c:171
bool ReadDB(DBHandle *handle, const char *key, void *dest, int destSz)
Definition: dbm_api.c:556
bool OverwriteDB(DBHandle *handle, const char *key, const void *value, size_t value_size, OverwriteCondition Condition, void *data)
Definition: dbm_api.c:566
bool DeleteDBCursor(DBCursor *cursor)
Definition: dbm_api.c:617
bool WriteDB(DBHandle *handle, const char *key, const void *src, int srcSz)
Definition: dbm_api.c:561
void CloseDB(DBHandle *handle)
Definition: dbm_api.c:472
bool NewDBCursor(DBHandle *handle, DBCursor **cursor)
Definition: dbm_api.c:588
@ dbid_locks
Definition: dbm_api.h:47
bool(* OverwriteCondition)(void *value, size_t value_size, void *data)
Definition: dbm_api_types.h:28
#define CF_BUFSIZE
Definition: definitions.h:50
#define CF_MAXVARSIZE
Definition: definitions.h:36
#define CF_BUFFERMARGIN
Definition: definitions.h:35
bool EvalContextPromiseLockCacheContains(const EvalContext *ctx, const char *key)
void EvalContextPromiseLockCacheRemove(EvalContext *ctx, const char *key)
bool EvalContextIsIgnoringLocks(const EvalContext *ctx)
void EvalContextPromiseLockCachePut(EvalContext *ctx, const char *key)
static void cleanup(void *generic_data)
Definition: fchmodat.c:56
bool FileSparseClose(int fd, const char *filename, bool do_sync, size_t total_bytes_written, bool last_write_was_hole)
Definition: file_lib.c:1426
bool FileSparseCopy(int sd, const char *src_name, int dd, const char *dst_name, size_t blk_size, size_t *total_bytes_written, bool *last_write_was_a_hole)
Definition: file_lib.c:1358
int errno
#define NULL
Definition: getopt1.c:56
pthread_mutex_t * cft_lock
Definition: global_mutex.c:37
size_t StringCopyTruncateAndHashIfNecessary(const char *const src, char *const dst, size_t dst_size)
Copy a string from src to dst, if src is too big, truncate and hash.
Definition: hash.c:662
const EVP_MD * HashDigestFromId(HashMethod type)
Returns pointer to an openssl digest struct.
Definition: hash.c:383
char * HashPrintSafe(char *dst, size_t dst_size, const unsigned char *digest, HashMethod type, bool use_prefix)
Definition: hash.c:612
#define CF_HOSTKEY_STRING_SIZE
Definition: hash.h:151
HashMethod
Definition: hash_method.h:36
void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
EVP_MD_CTX * EVP_MD_CTX_new(void)
static CfLock CfLockNull(void)
Definition: locks.c:780
void BackupLockDatabase(void)
Definition: locks.c:1144
static void CopyLockDatabaseAtomically(const char *from, const char *to, const char *from_pretty_name, const char *to_pretty_name)
Definition: locks.c:1084
static int RemoveLock(const char *name)
Definition: locks.c:342
#define CF_MAXLOCKNUM
Definition: locks.c:54
static void LocksCleanup(void)
Definition: locks.c:482
static void PromiseTypeString(char *dst, size_t dst_size, const Promise *pp)
Definition: locks.c:508
void YieldCurrentLock(CfLock lock)
Definition: locks.c:948
void PromiseRuntimeHash(const Promise *pp, const char *salt, unsigned char digest[EVP_MAX_MD_SIZE+1], HashMethod type)
Definition: locks.c:640
static CfLockStack * LOCK_STACK
Definition: locks.c:71
static int WriteLock(const char *name)
Definition: locks.c:222
struct CfLockStack_ CfLockStack
static void RegisterLockCleanup(void)
Definition: locks.c:496
static void RvalDigestUpdate(EVP_MD_CTX *context, Rlist *rp)
Definition: locks.c:616
static bool WriteLockData(CF_DB *dbp, const char *lock_id, LockData *lock_data)
Definition: locks.c:193
static CfLock CfLockNew(const char *last, const char *lock, bool is_dummy)
Definition: locks.c:771
static void VerifyThatDatabaseIsNotCorrupt_once(void)
Definition: locks.c:100
#define LOG_LOCK_EXIT(__lock, __lock_sum, __lock_data)
Definition: locks.c:60
void ReleaseCriticalSection(const char *section_id)
Definition: locks.c:447
static bool WriteLockDataCurrent(CF_DB *dbp, const char *lock_id)
Definition: locks.c:212
void WaitForCriticalSection(const char *section_id)
Definition: locks.c:385
#define LOG_LOCK_OP(__lock, __lock_sum, __lock_data)
Definition: locks.c:62
void CloseLock(CF_DB *dbp)
Definition: locks.c:146
#define CF_LOCKHORIZON
Definition: locks.c:53
CF_DB * OpenLock()
Definition: locks.c:132
static bool NoOrObsoleteLock(LockData *entry, size_t entry_size, size_t *max_old)
Definition: locks.c:368
static CfLockStack * PopLock()
Definition: locks.c:83
static time_t FindLockTime(const char *name)
Definition: locks.c:240
static void RestoreLockDatabase(void)
Definition: locks.c:1067
#define CF_CRITIAL_SECTION
Definition: locks.c:56
static void PushLock(char *lock, char *last)
Definition: locks.c:73
void YieldCurrentLockAndRemoveFromCache(EvalContext *ctx, CfLock lock, const char *operand, const Promise *pp)
Definition: locks.c:1010
static void RemoveDates(char *s)
Definition: locks.c:275
static pthread_once_t lock_cleanup_once
Definition: locks.c:154
CfLock AcquireLock(EvalContext *ctx, const char *operand, const char *host, time_t now, int ifelapsed, int expireafter, const Promise *pp, bool ignoreProcesses)
Definition: locks.c:789
static time_t FindLock(char *last)
Definition: locks.c:460
static bool KillLockHolder(const char *lock)
Definition: locks.c:557
static void VerifyThatDatabaseIsNotCorrupt(void)
Definition: locks.c:126
#define LOG_LOCK_ENTRY(__lock, __lock_sum, __lock_data)
Definition: locks.c:58
void PurgeLocks(void)
Definition: locks.c:1161
void GetLockName(char *lockname, const char *locktype, const char *base, const Rlist *params)
Definition: locks.c:1024
@ LogDebug
Definition: log.h:34
LogLevel LogGetGlobalLevel(void)
Definition: logging.c:581
const char * GetErrorStr(void)
Definition: logging.c:275
void Log(LogLevel level, const char *fmt,...)
Definition: logging.c:409
@ LOG_MOD_LOCKS
Definition: logging.h:64
@ LOG_LEVEL_ERR
Definition: logging.h:42
@ LOG_LEVEL_NOTICE
Definition: logging.h:44
@ LOG_LEVEL_DEBUG
Definition: logging.h:47
@ LOG_LEVEL_WARNING
Definition: logging.h:43
@ LOG_LEVEL_CRIT
Definition: logging.h:41
@ LOG_LEVEL_VERBOSE
Definition: logging.h:46
@ LOG_LEVEL_INFO
Definition: logging.h:45
#define ProgrammingError(...)
Definition: misc_lib.h:33
#define ThreadUnlock(m)
Definition: mutex.h:33
#define ThreadLock(m)
Definition: mutex.h:32
#define O_BINARY
Definition: platform.h:1001
unsigned int sleep(unsigned int seconds)
#define DEV_BSIZE
Definition: platform.h:828
const Bundle * PromiseGetBundle(const Promise *pp)
Definition: policy.c:2671
time_t GetProcessStartTime(pid_t pid)
Definition: process_aix.c:53
#define PROCESS_START_TIME_UNKNOWN
Definition: process_lib.h:34
bool GracefulTerminate(pid_t pid, time_t process_start_time)
Definition: process_unix.c:241
FnCall * RlistFnCallValue(const Rlist *rlist)
Definition: rlist.c:105
char * RlistScalarValue(const Rlist *rlist)
Definition: rlist.c:83
size_t SeqLength(const Seq *seq)
Length of the sequence.
Definition: sequence.c:354
static void * SeqAt(const Seq *seq, int i)
Definition: sequence.h:57
void CanonifyNameInPlace(char *s)
Definition: string_lib.c:1574
#define STARTSWITH(str, start)
Definition: string_lib.h:49
size_t strlcat(char *dst, const char *src, size_t siz)
Definition: strlcat.c:36
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:34
char * strstr(const char *haystack, const char *needle)
Definition: strstr.c:35
char * name
Definition: policy.h:74
char * ns
Definition: policy.h:75
char last[4096]
Definition: locks.c:67
struct CfLockStack_ * previous
Definition: locks.c:68
char lock[4096]
Definition: locks.c:66
char * last
Definition: cf3.defs.h:894
bool is_dummy
Definition: cf3.defs.h:896
char * lock
Definition: cf3.defs.h:895
Rval rval
Definition: policy.h:133
char * lval
Definition: policy.h:132
Definition: fncall.h:31
char * name
Definition: fncall.h:32
Rlist * args
Definition: fncall.h:33
time_t time
Definition: db_structs.h:28
time_t process_start_time
Definition: db_structs.h:29
pid_t pid
Definition: db_structs.h:27
Bundle * parent_bundle
Definition: policy.h:101
char * name
Definition: policy.h:103
PromiseType * parent_promise_type
Definition: policy.h:111
Seq * conlist
Definition: policy.h:117
char * comment
Definition: policy.h:114
char * promiser
Definition: policy.h:115
Definition: rlist.h:35
Rval val
Definition: rlist.h:36
Rlist * next
Definition: rlist.h:37
RvalType type
Definition: cf3.defs.h:616
void * item
Definition: cf3.defs.h:615
int GetUptimeSeconds(time_t now)
Definition: sysinfo.c:3061