From d8ea4f87e3e5a44985f961a2def83612b5c90c9a Mon Sep 17 00:00:00 2001
From: An-Cheng Huang <ancheng@vyatta.com>
Date: Mon, 13 Dec 2010 15:15:09 -0800
Subject: make the library croak when internal error occurs in perl context.

* extend output/assert mechanism and simplify code.
* when handling internal error, automatically detect perl context and croak if the library is used from perl.
---
 Makefile.am           |   1 +
 src/cli_cstore.h      |   1 +
 src/cstore/cstore.cpp | 114 +++++++++++++++++++++++++++++++++++++++-----------
 src/cstore/cstore.hpp |  35 ++++++++++------
 4 files changed, 113 insertions(+), 38 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index bc4bb78..a932d5e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -30,6 +30,7 @@ src_libvyatta_cfg_la_LIBADD += /usr/lib/libgio-2.0.la
 src_libvyatta_cfg_la_LIBADD += -lboost_system
 src_libvyatta_cfg_la_LIBADD += -lboost_filesystem
 src_libvyatta_cfg_la_LIBADD += -lapt-pkg
+src_libvyatta_cfg_la_LIBADD += -lperl
 src_libvyatta_cfg_la_LDFLAGS = -version-info 1:0:0
 src_libvyatta_cfg_la_SOURCES = src/cli_parse.y src/cli_def.l src/cli_val.l
 src_libvyatta_cfg_la_SOURCES += src/cli_new.c src/cli_path_utils.c
diff --git a/src/cli_cstore.h b/src/cli_cstore.h
index c03bddf..54da657 100644
--- a/src/cli_cstore.h
+++ b/src/cli_cstore.h
@@ -116,6 +116,7 @@ typedef struct {
 /* extern variables */
 extern void *var_ref_handle;
 extern FILE *out_stream;
+extern FILE *err_stream;
 
 /* note that some functions may be used outside the actual CLI operations,
  * so output may not have been initialized. nop in such cases.
diff --git a/src/cstore/cstore.cpp b/src/cstore/cstore.cpp
index 0ff8ba2..2aefed6 100644
--- a/src/cstore/cstore.cpp
+++ b/src/cstore/cstore.cpp
@@ -1929,11 +1929,16 @@ Cstore::output_user(const char *fmt, ...)
 {
   va_list alist;
   va_start(alist, fmt);
-  if (out_stream) {
-    vfprintf(out_stream, fmt, alist);
-  } else {
-    vprintf(fmt, alist);
-  }
+  voutput_user(out_stream, stdout, fmt, alist);
+  va_end(alist);
+}
+
+void
+Cstore::output_user_err(const char *fmt, ...)
+{
+  va_list alist;
+  va_start(alist, fmt);
+  voutput_user(err_stream, stderr, fmt, alist);
   va_end(alist);
 }
 
@@ -1942,29 +1947,30 @@ Cstore::output_internal(const char *fmt, ...)
 {
   va_list alist;
   va_start(alist, fmt);
+  voutput_internal(fmt, alist);
+  va_end(alist);
+}
 
-  int fdout = -1;
-  FILE *fout = NULL;
-  do {
-    if ((fdout = open(C_LOGFILE_STDOUT.c_str(),
-                      O_WRONLY | O_CREAT, 0660)) == -1) {
-      break;
-    }
-    if (lseek(fdout, 0, SEEK_END) == ((off_t) -1)) {
-      break;
-    }
-    if ((fout = fdopen(fdout, "a")) == NULL) {
-      break;
-    }
-    vfprintf(fout, fmt, alist);
-  } while (0);
+void
+Cstore::exit_internal(const char *fmt, ...)
+{
+  va_list alist;
+  va_start(alist, fmt);
+  vexit_internal(fmt, alist);
   va_end(alist);
-  if (fout) {
-    fclose(fout);
-    // fdout is implicitly closed
-  } else if (fdout >= 0) {
-    close(fdout);
+}
+
+void
+Cstore::assert_internal(bool cond, const char *fmt, ...)
+{
+  if (cond) {
+    return;
   }
+
+  va_list alist;
+  va_start(alist, fmt);
+  vexit_internal(fmt, alist);
+  va_end(alist);
 }
 
 
@@ -2872,3 +2878,61 @@ Cstore::print_str_vec(const char *pre, const char *post,
   output_user("%s", post);
 }
 
+void
+Cstore::voutput_user(FILE *out, FILE *dout, const char *fmt, va_list alist)
+{
+  if (out) {
+    vfprintf(out, fmt, alist);
+  } else if (dout) {
+    vfprintf(dout, fmt, alist);
+  } else {
+    vprintf(fmt, alist);
+  }
+}
+
+void
+Cstore::voutput_internal(const char *fmt, va_list alist)
+{
+  int fdout = -1;
+  FILE *fout = NULL;
+  do {
+    if ((fdout = open(C_LOGFILE_STDOUT.c_str(),
+                      O_WRONLY | O_CREAT, 0660)) == -1) {
+      break;
+    }
+    if (lseek(fdout, 0, SEEK_END) == ((off_t) -1)) {
+      break;
+    }
+    if ((fout = fdopen(fdout, "a")) == NULL) {
+      break;
+    }
+    vfprintf(fout, fmt, alist);
+  } while (0);
+  if (fout) {
+    fclose(fout);
+    // fdout is implicitly closed
+  } else if (fdout >= 0) {
+    close(fdout);
+  }
+}
+
+void
+Cstore::vexit_internal(const char *fmt, va_list alist)
+{
+  char buf[256];
+  vsnprintf(buf, 256, fmt, alist);
+  output_internal("%s\n", buf);
+  if (Perl_get_context()) {
+    /* we're in a perl context. do a croak to provide more information.
+     * note that the message should not end in "\n", or the croak message
+     * will be truncated for some reason.
+     */
+    Perl_croak_nocontext("%s", buf);
+    // does not return
+  } else {
+    // output error message and exit
+    output_user_err("%s\n", buf);
+    exit(1);
+  }
+}
+
diff --git a/src/cstore/cstore.hpp b/src/cstore/cstore.hpp
index 872fe1e..9a039af 100644
--- a/src/cstore/cstore.hpp
+++ b/src/cstore/cstore.hpp
@@ -16,26 +16,27 @@
 
 #ifndef _CSTORE_H_
 #define _CSTORE_H_
+#include <cstdarg>
 #include <vector>
 #include <string>
 #include <tr1/unordered_map>
 
 #include <cli_cstore.h>
 
-#define exit_internal(fmt, args...) do \
-  { \
-    output_internal("[%s:%d] " fmt, __FILE__, __LINE__ , ##args); \
-    exit(-1); \
-  } while (0);
+/* declare perl internal functions. just need these two so don't include
+ * all the perl headers.
+ */
+extern "C" void Perl_croak_nocontext(const char* pat, ...)
+  __attribute__((noreturn))
+  __attribute__((format(__printf__,1,2)))
+  __attribute__((nonnull(1)));
+
+extern "C" void* Perl_get_context(void)
+  __attribute__((warn_unused_result));
 
-#define ASSERT_IN_SESSION do \
-  { \
-    if (!inSession()) { \
-      output_user("Internal error: calling %s() without config session\n", \
-                  __func__); \
-      exit_internal("calling %s() without config session\n", __func__); \
-    } \
-  } while (0);
+#define ASSERT_IN_SESSION assert_internal(inSession(), \
+                            "calling %s() without config session", \
+                            __func__);
 
 
 /* macros for saving/restoring paths.
@@ -300,7 +301,10 @@ public:
 protected:
   ////// functions for subclasses
   void output_user(const char *fmt, ...);
+  void output_user_err(const char *fmt, ...);
   void output_internal(const char *fmt, ...);
+  void exit_internal(const char *fmt, ...);
+  void assert_internal(bool cond, const char *fmt, ...);
 
 private:
   ////// member class
@@ -480,6 +484,11 @@ private:
   void shell_escape_squotes(string& str);
   void print_str_vec(const char *pre, const char *post,
                      const vector<string>& vec, const char *quote);
+
+  // output functions
+  void voutput_user(FILE *out, FILE *dout, const char *fmt, va_list alist);
+  void voutput_internal(const char *fmt, va_list alist);
+  void vexit_internal(const char *fmt, va_list alist);
 };
 
 #endif /* _CSTORE_H_ */
-- 
cgit v1.2.3