Monday, September 16, 2013

Strings without escape characters

Does this look familiar?
        \"Action\":\"Upload\",
        \"FromVersion\":\"ver1\",
        \"ToVersion\":\"ver2\"

Wouldn't it be awesome to be able to write like this instead?
        "Action":"Upload",
        "FromVersion":"ver1",
        "ToVersion":"ver2"

Escape characters exists for a simple reason. It says "do not interpret the character after as it should be interpreted". But if we are writing Json we should put escape character way too often. The resulting string could be unreadable. But a simple solution exists:

  1. #define MAKE_STRING(...)  #__VA_ARGS__

...

  1. const char *json(MAKE_STRING( {
  2.         "Action":"Upload",
  3.         "FromVersion":"ver1",
  4.         "ToVersion":"ver2" } );

Done! That simple. Here we use a technique called stringification. It accepts a variadic macro, variable number of arguments, in other words. And it stringifies them! Meaning it produces strings. So all the parameters are stringified and then concatenated separately, because two string in C are always concatenated if put next to each other, i.e.

  1. "foo" "bar"

will produce "foobar". Note, that I'm talking about strings here, not the pointers to array of chars or string objects from C++. No, pure strings, such as "I'm a string".

This technique should work on all modern compilers. Was tested on GCC, Clang, MS compiler.

Simple automated email alerts

You have a remote system which you want to know the state of. Or you want to monitor it. Or you want to be notified when something happens. But of course you don't want to constantly log into the system and check. There can be dozens parameters to check. You might forget one or two. And that is not a true zen if you have to do this manually.

So, here comes mailing notifications. That'd be awesome to get an email once a situation has occurred. But we don't want to spend time and money on a SMTP server. And we just want to monitor our home server. The solution is easy - use any of your GMail accounts (it can be any provider, but I'm here talking about GMail) or register a new one.

Then write a script. Copy paste from here and edit.

alert.sh
  1. #!/bin/sh
  2. source "${LOCAL_CONFIG_DIR}/mailx.config.sh"
  3. SUBJECT="Alert! Raspberry Pi Home. rootfs > ${2}%"
  4. TEXT="Alert on Raspberry Pi home. In the last ${1} seconds rootfs passed the threshold of ${2} and reached ${3}%."
  5. echo ${TEXT} | \
  6. env MAILRC=/dev/null from="${SENDER}" smtp-auth-user="${SENDER}" smtp-auth-password="${SENDER_PASSWORD}" \
  7. mailx -n \
  8.     -S smtp-use-starttls \
  9.     -S smtp-auth=login \
  10.     -S smtp="$SMTP_SERVER" \
  11.     -s "${SUBJECT}" "${RECEIVER}"

and here is a mailx.config.sh which is included at line #3
  1. RECEIVER=yourself@gmail.com
  2. SENDER=alert...@gmail.com
  3. SENDER_PASSWORD="alert...@gmail.com password"
  4. SMTP_SERVER=smtp://smtp.gmail.com:587

I separated config from the alert script because you can have a number of alerts which you want to use the same config with.

This is the simplest automated notification functionality. It sends a notification in plain text. There are ways to attach files (read man mailx) and there are ways to include html as a body. For my purposes it was perfectly fine, so I didn't bother myself with decorations.


UPDATE [September 30, 2013]

Multiline emails can be sent using this technique:

  1. (
  2. cat << EOF
  3. Message from hostname '$HOSTNAME'.
  4.  
  5. This system is reaching/exceeding the defined threshold value ($ALERT_THRESHOLD) during the last '$ALERT_TIMEINTVL' seconds.
  6.  
  7. The current value is: $current_value
  8.  
  9. Please take proper actions to correct this situation.
  10. EOF
  11. ) | \
  12. env MAILRC=/dev/null from="${SENDER}" smtp-auth-user="${SENDER}" smtp-auth-password="${SENDER_PASSWORD}" \
  13. mailx -n \
  14.     -S smtp-use-starttls \
  15.     -S smtp-auth=login \
  16.     -S smtp="$SMTP_SERVER" \
  17.     -s "${SUBJECT}" "${RECEIVER}"

Thursday, September 12, 2013

Exception like stack trace in pure C

All of us have seen stack traces printed out after an exception has been thrown.

Java:
Exception in thread "main" java.lang.NullPointerException
        at com.example.myproject.Book.getTitle(Book.java:16)
        at com.example.myproject.Author.getBookTitles(Author.java:25)
        at com.example.myproject.Bootstrap.main(Bootstrap.java:14)

Python:
Traceback (most recent call last):
  File "test.py", line 8, in b
    c()
  File "test.py", line 13, in c
    assert False
AssertionError

You may find a plenty of information about other languages online.

Stack traces are almost always helpful when debugging. But what if exceptions just don't exist in the language or are forbidden? Well, I don't sympathize C++ exceptions and I don't appreciate exceptions over error codes in other languages, but this is out of the scope of the blog post.

So, let's be more specific. We are in the pure C world. We can't use exceptions. Now what? Do something ugly like:

  1. printf("OMG, something happened\n");
  2. return;

What if we need to finish executing the program if that something happens?

  1. char* ar = malloc(23423423);
  2. if(ar == NULL)
  3. {
  4.  printf("oh, ar can't be allocated\n");
  5.  exit(errorcode);
  6. }

What if there are a number of functions where ar can be allocated (not the same ar, but just local to function ar)? What if we reuse ar in such a way that it is assigned to a different variable and that malloced again, or realloced? What if we want to easily weed errors out of a normal output? Oh, we could use

  1. fprintf(stderr, "my error");

But what if we now want to change output from stderr to another stream? Or what if we want a uniform format for all of our errors? Or what if we just want to track our error from the origin all the way up to the place where it was called from? Imagine an XML or Json parser which blows up somewhere in a node parsing? What will that error from inside the depths of the parser tell you? Nothing at all, it is generic. It would be much lovelier to see who called that parser and with what arguments. This is where stack trace would be handy. And here is what we are aiming for in C:

Error: example.c:35 (my_function) - [0000001b] custom error
 ----> someFile.c:50 (super_hero_function) - [0000001b]
 ----> main.c:163 (main) - [00000001]

This is an example of a proper stack trace. It has an origin in example.c::my_function() and it traces all the way back to main.c::main() through someFile.c::super_hero_function(), but it also outputs a return result of each of those functions on its way.

This is a code which serves this kind of output:

  1. //
  2. // developed by Sergey Markelov (09-2013)
  3. //
  4. #ifndef GENERIC_LOGGER
  5. #define GENERIC_LOGGER
  6. #include <stdio.h>
  7. extern FILE *errStream;
  8. extern FILE *outStream;
  9. #define ERR_STREAM              errStream
  10. #define OUT_STREAM              outStream
  11. #define ERROR_PREFIX            "Error: "
  12. #define STANDARD_PREFIX         ""
  13. #define ERROR_CONTINUE_PREFIX   " ----> "
  14. #define STRINGIFY(x) #x
  15. #define TOSTRING(x)  STRINGIFY(x)
  16. #define _LOG(stream, prefix, format, ...) \
  17.     { fprintf(stream, "%s (%s) - " format "\n", prefix __FILE__ ":" TOSTRING(__LINE__), __func__, ##__VA_ARGS__); }
  18. #define LogError(format, ...) \
  19.     { _LOG(ERR_STREAM, ERROR_PREFIX, format, ##__VA_ARGS__); }
  20. #define Log(format, ...) \
  21.     { _LOG(OUT_STREAM, STANDARD_PREFIX, format, ##__VA_ARGS__); }
  22. //
  23. // @brief this macro originates the error. Functions down the stack should use
  24. //        ContinueErrorEx() or ContinueError() to propagate the error behavior.
  25. //
  26. //        In the log it will look like:
  27. //
  28. //        Error: example.c:35 (my_function) - [0000001b] custom error
  29. //         ----> someFile.c:50 (process) - [0000001b]
  30. //         ----> main.c:163 (main) - [00000001]
  31. //
  32. // @param result the value which needs to be logged
  33. // @param resultSpecifier printf() specifier for the result. Ex. "%d" means @c result is of type @c int
  34. //        you can also use "0x%08x" to make it Hex
  35. // @param format, ... - custom formatted message
  36. //
  37. #define OriginateErrorEx(result, resultSpecifier, format, ...) \
  38.     { LogError("[" resultSpecifier "] " format, result, ##__VA_ARGS__); return result; }
  39. //
  40. // @see OriginateErrorEx
  41. //
  42. #define OriginateError(result, resultSpecifier) \
  43.     { LogError("[" resultSpecifier "] ", result); return result; }
  44. //
  45. // @see OriginateErrorEx
  46. //
  47. #define ContinueErrorEx(result, resultSpecifier, format, ...) \
  48.     { _LOG(ERR_STREAM, ERROR_CONTINUE_PREFIX, \
  49.            "[" resultSpecifier "] " format, result, ##__VA_ARGS__); return result; }
  50. //
  51. // @see OriginateErrorEx
  52. //
  53. #define ContinueError(result, resultSpecifier) \
  54.     { _LOG(ERR_STREAM, ERROR_CONTINUE_PREFIX, \
  55.            "[" resultSpecifier "] ", result); return result; }
  56. #endif

and here is an example usage:

  1. //
  2. // developed by Sergey Markelov (09-2013)
  3. //
  4. #include "logger.h"
  5. //
  6. // defined in logger.h
  7. //
  8. FILE *errStream;
  9. FILE *outStream;
  10. static int blowUp()
  11. {
  12.     if(ftell(NULL) == -1) OriginateErrorEx(-1, "%d", "ftell() caused errno %d - '%s'", errno, strerror(errno));
  13.     return 0;
  14. }
  15. int main(void)
  16. {
  17.     int res;
  18.     errStream = stderr;
  19.     outStream = stdout;
  20.     res = blowUp();
  21.     if(res != 0) ContinueError(res, "%d");
  22.     return 0;
  23. }

ftell will fail on NULL file stream and this stack trace will be outputed to stderr:

Error: main.c:15 (blowUp) - [-1] ftell() caused errno [3450] - 'Descriptor is not valid'
 ----> main.c:27 (main) - [-1]

As you may see we have everything we need to understand what exactly happened and when and who caused that error to happen.

In the code above I used variadic macros. That version is GNU specific and you can't find it in a compiler from MS. Clang supports it out of the box unless you specify to warn about that with -Wgnu. GCC, of course, supports it.

When I say that MS doesn't support variadic macros, that is not to be confused with the full GNU support when you separate format part from the actual variadic part. This way, we can do tricky things like that:

  1. #define _LOG(stream, prefix, format, ...) \
  2.     { fprintf(stream, "%s (%s) - " format "\n", prefix __FILE__ ":" TOSTRING(__LINE__), __func__, ##__VA_ARGS__); }

that is the most generic logging function from the above. It constructs the format string concatenating
"%s (%s) - " with format which is passed as an argument to a macro.

Also I used __LINE__ stringification discussed here.

All that logging mechanism is pure C style macros, thus the original code after preprocessing will be as fast as ordinary fprintf's here and there in your code. So, at the run time we pay no extra cost for a lot of extra functionality!

And all you have to do to use this logging mechanism is to include the h file and to declare errStream and outStream. You can link those to stderr and stdout. Or you can fopen a file and link the stream to there.