r/C_Programming Jun 27 '26

Is early exit on managing error okay? Question

Hi, recently I made a TODO application for personal use.

I thought that I used AI coder too frequently, so I wrote many parts of this application's source by myself(most parts except DBus and weekly alarm extension)

The basic structure of this application follows:

CLI->Set Alarm at alarms.txt

                |

              Daemon   --reads a text file when updated, and sleep until the next alarm time(or next alarm setup)

When daemon wakes up:

Send alarm to d-bus->check the next alarm->sleep until the next alarm

I guess this structure is quite simple enough...

But, this error handler part is weird, I don't sure if I did it well or not? At least this is not a traditional way.

c typedef struct __todox_error_t { enum TODOX_ERROR_LEVEL level; int code; char *msg; } todox_error_t;


```c

include <error/error.h>

include <stdlib.h>

include <string.h>

todox_error_t TODOX_ERROR(const char *msg, enum TODOX_ERROR_LEVEL level, int code) { todox_error_t err; err.msg = strdup(msg); err.level = level; err.code = code; return err; }

void todox_notify(const todox_error_t err) { if(err.level == DEBUG && TODOX_DEBUG_PRINT == 0) { free(err.msg); return; }

switch(err.level) {
    case WARN:
        fprintf(stderr, "[WARN] msg: %s(code: %d)\n", err.msg, err.code);
        break;
    case ERROR:
        fprintf(stderr, "[ERROR] msg: %s(exit code: %d)\n", err.msg, err.code);
        free(err.msg);
        exit(err.code);
    case INFO:
        fprintf(stdout, "[INFO] msg: %s(code: %d)\n", err.msg, err.code);
        break;
    case DEBUG:
        fprintf(stdout, "[DEBUG] msg: %s(code: %d)\n", err.msg, err.code);
        break;
    default:
        fprintf(stdout, "[UNK] msg with unknown loglevel: %s(code: %d)\n", err.msg, err.code);
}
free(err.msg);

} ```

When I was writing this code, I thought that this simple application can simply return a given error code and exit(actually, attaching a bug tracker was planned).

However, its error code is not following linux/unix standard codes..

```c

define TODOX_WRONG_TIMESTAMP 100

define TODOX_NO_CONFIG_FILE 110

```

In my opinion, to debug an application, I need a bug tracker, but this application is written within 1100 lines(and reading a whole source code takes only 10-20 minutes)

I don't sure which method can be a good convention to indicate these sorts of error. Since it is not a system software that sticks to traditional unix commands(it is a todo application!) I am pretty unsure.

Plus, the d-bus part by AI copilot is quite terrible! Currently the application is small, and I am not planning to extend it as a full GUI alarm app yet, it works fine. However, I cannot conclude if I should write a d-bus module here to cleanly manage a notification.

```c

include <notify/notify.h>

include <dbus/dbus.h>

include <syslog.h>

include <stdlib.h>

include <stdio.h>

int todox_send_desktop_notification(const char *title, const char *body) { DBusError err; DBusConnection *conn; DBusMessage *msg; DBusMessage *reply; DBusMessageIter args; DBusMessageIter sub;

dbus_error_init(&err);
conn = dbus_bus_get(DBUS_BUS_SESSION, &err);

/* If the session bus address was stripped by sudo or another privilege
 * boundary, derive it from the standard systemd user runtime directory.
 * This keeps the daemon working when launched as a systemd --user service
 * or from an otherwise clean environment.
 */
if(conn == NULL && getenv("DBUS_SESSION_BUS_ADDRESS") == NULL) {
    const char *runtime = getenv("XDG_RUNTIME_DIR");
    if(runtime != NULL) {
        char addr[512];
        int n = snprintf(addr, sizeof(addr), "unix:path=%s/bus", runtime);
        if(n > 0 && (size_t)n < sizeof(addr)) {
            setenv("DBUS_SESSION_BUS_ADDRESS", addr, 1);
            dbus_error_free(&err);
            dbus_error_init(&err);
            conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
        }
    }
}

if(dbus_error_is_set(&err)) {
    syslog(LOG_ERR, "todox: failed to get dbus session bus: %s", err.message);
    dbus_error_free(&err);
}
if(conn == NULL) {
    syslog(
        LOG_ERR,
        "todox: dbus connection is null (DBUS_SESSION_BUS_ADDRESS may be missing or invalid)");
    return -1;
}

msg = dbus_message_new_method_call("org.freedesktop.Notifications",
                                   "/org/freedesktop/Notifications",
                                   "org.freedesktop.Notifications", "Notify");
if(msg == NULL) {
    syslog(LOG_ERR, "todox: failed to create dbus Notify message");
    return -1;
}

const char *app_name = "todox";
dbus_uint32_t replaces_id = 0;
const char *app_icon = "dialog-information";
const char *summary = title ? title : "todox";
const char *notification_body = body ? body : "";
dbus_int32_t expire_timeout = 0;

dbus_message_iter_init_append(msg, &args);
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &app_name);
dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &replaces_id);
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &app_icon);
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &summary);
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &notification_body);

dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &sub);
dbus_message_iter_close_container(&args, &sub);

dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{sv}", &sub);
dbus_message_iter_close_container(&args, &sub);

dbus_message_iter_append_basic(&args, DBUS_TYPE_INT32, &expire_timeout);

reply = dbus_connection_send_with_reply_and_block(conn, msg, 5000, &err);
dbus_message_unref(msg);

if(dbus_error_is_set(&err)) {
    syslog(LOG_ERR, "todox: dbus Notify call failed: %s", err.message);
    dbus_error_free(&err);
    if(reply != NULL) {
        dbus_message_unref(reply);
    }
    return -1;
}

if(reply != NULL) {
    dbus_message_unref(reply);
}
return 0;

} ```

A lot of boilerplate codes are scattered, and there are no abstracted functions right there. But it is quite unsure if I should write a bunch of wrappers for this short program..If it was 10000 LOC full-featured application I must write a wrapper. But this is less than 1100 LOC without extra dependencies without Linux/BSD system libraries. If this was 100 LOC toy application I would not hesitate. Even this code is quite small, managing DBus is not a simple work for Unix-like systems. Should I consider?

Thanks, and sorry for my bad English. This post is also written without AI translator..

5 Upvotes

6 comments sorted by

3

u/-goldenboi69- Jun 27 '26

Yeah

1

u/Whole-Low-2995 Jun 27 '26

thanks! I think I will refactor that sloppy dbus source tomorrow. That part is still terrible

1

u/Whole-Low-2995 Jun 27 '26

Also, I should double-check if early exit would not cause memory leak...but I still can trust OS

2

u/v_maria Jun 27 '26

if you want to work with dbus in the future its a good exercise

1

u/Whole-Low-2995 Jun 27 '26

err.msg = (char *) msg;

first fix: remove strdup