I was looking at some sample code here and it had this for error handling:

#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)

If I remember correctly do {} while (0) would execute only once, so what is the point of this?

Put simply, the point is to enforce a trailing semicolon on calls to the macro without losing the massive benefits of having the macro wrapped in its own scope.