-- 8ch indent, no tabs
+- 8ch indent, no tabs, except for files in man/ which are 2ch indent,
+ and still no tabs
+
+- We prefer /* comments */ over // comments, please. This is not C++, after
+ all. (Yes we know that C99 supports both kinds of comments, but still,
+ please!)
- Don't break code lines too eagerly. We do *not* force line breaks at
80ch, all of today's screens should be much larger than that. But
- For robustness reasons, destructors should be able to destruct
half-initialized objects, too
-- Error codes are returned as negative Exxx. i.e. return -EINVAL. There
+- Error codes are returned as negative Exxx. e.g. return -EINVAL. There
are some exceptions: for constructors, it is OK to return NULL on
OOM. For lookup functions, NULL is fine too for "not found".
doing something wrong!
- Stay uniform. For example, always use "usec_t" for time
- values. Do not usec mix msec, and usec and whatnot.
+ values. Do not mix usec and msec, and usec and whatnot.
- Make use of _cleanup_free_ and friends. It makes your code much
nicer to read!
But it is OK if you do not.
+- Single-line "if" blocks should not be enclosed in {}. Use this:
+
+ if (foobar)
+ waldo();
+
+ instead of this:
+
+ if (foobar) {
+ waldo();
+ }
+
- Do not write "foo ()", write "foo()".
- Please use streq() and strneq() instead of strcmp(), strncmp() where applicable.
no speed benefit, and on calls like printf() "float"s get promoted
to "double"s anyway, so there is no point.
-- Do not invoke functions when you allocate variables on the stack. Wrong:
+- Do not mix function invocations with variable definitions in one
+ line. Wrong:
{
int a = foobar();
- Do not use types like "short". They *never* make sense. Use ints,
longs, long longs, all in unsigned+signed fashion, and the fixed
- size types uint32_t and so on, as well as size_t, but nothing else.
+ size types uint32_t and so on, as well as size_t, but nothing
+ else. Do not use kernel types like u32 and so on, leave that to the
+ kernel.
- Public API calls (i.e. functions exported by our shared libraries)
must be marked "_public_" and need to be prefixed with "sd_". No
is_main_thread() to detect whether the calling thread is the main
thread.
-- Option parsing:
+- Command line option parsing:
- Do not print full help() on error, be specific about the error.
- Do not print messages to stdout on error.
- Do not POSIX_ME_HARDER unless necessary, i.e. avoid "+" in option string.
+
+- Do not write functions that clobber call-by-reference variables on
+ failure. Use temporary variables for these cases and change the
+ passed in variables only on success.
+
+- When you allocate a file descriptor, it should be made O_CLOEXEC
+ right from the beginning, as none of our files should leak to forked
+ binaries by default. Hence, whenever you open a file, O_CLOEXEC must
+ be specified, right from the beginning. This also applies to
+ sockets. Effectively this means that all invocations to:
+
+ a) open() must get O_CLOEXEC passed
+ b) socket() and socketpair() must get SOCK_CLOEXEC passed
+ c) recvmsg() must get MSG_CMSG_CLOEXEC set
+ d) F_DUPFD_CLOEXEC should be used instead of F_DUPFD, and so on
+
+- We never use the POSIX version of basename() (which glibc defines it in
+ libgen.h), only the GNU version (which glibc defines in string.h).
+ The only reason to include libgen.h is because dirname()
+ is needed. Everytime you need that please immediately undefine
+ basename(), and add a comment about it, so that no code ever ends up
+ using the POSIX version!
+
+- Use the bool type for booleans, not integers. One exception: in public
+ headers (i.e those in src/systemd/sd-*.h) use integers after all, as "bool"
+ is C99 and in our public APIs we try to stick to C89 (with a few extension).
+
+- When you invoke certain calls like unlink(), or mkdir_p() and you
+ know it is safe to ignore the error it might return (because a later
+ call would detect the failure anyway, or because the error is in an
+ error path and you thus couldn't do anything about it anyway), then
+ make this clear by casting the invocation explicitly to (void). Code
+ checks like Coverity understand that, and will not complain about
+ ignored error codes. Hence, please use this:
+
+ (void) unlink("/foo/bar/baz");
+
+ instead of just this:
+
+ unlink("/foo/bar/baz");
+
+- Don't invoke exit(), ever. It is not replacement for proper error
+ handling. Please escalate errors up your call chain, and use normal
+ "return" to exit from the main function of a process. If you
+ fork()ed off a child process, please use _exit() instead of exit(),
+ so that the exit handlers are not run.
+
+- Please never use dup(). Use fcntl(fd, F_DUPFD_CLOEXEC, 3)
+ instead. For two reason: first, you want O_CLOEXEC set on the new fd
+ (see above). Second, dup() will happily duplicate your fd as 0, 1,
+ 2, i.e. stdin, stdout, stderr, should those fds be closed. Given the
+ special semantics of those fds, it's probably a good idea to avoid
+ them. F_DUPFD_CLOEXEC with "3" as parameter avoids them.
+
+- When you define a destructor or unref() call for an object, please
+ accept a NULL object and simply treat this as NOP. This is similar
+ to how libc free() works, which accepts NULL pointers and becomes a
+ NOP for them. By following this scheme a lot of if checks can be
+ removed before invoking your destructor, which makes the code
+ substantially more readable and robust.
+
+- Related to this: when you define a destructor or unref() call for an
+ object, please make it return the same type it takes and always
+ return NULL from it. This allows writing code like this:
+
+ p = foobar_unref(p);
+
+ which will always work regardless if p is initialized or not, and
+ guarantees that p is NULL afterwards, all in just one line.
+
+- Use alloca(), but never forget that it is not OK to invoke alloca()
+ within a loop or within function call parameters. alloca() memory is
+ released at the end of a function, and not at the end of a {}
+ block. Thus, if you invoke it in a loop, you keep increasing the
+ stack pointer without ever releasing memory again. (VLAs have better
+ behaviour in this case, so consider using them as an alternative.)
+ Regarding not using alloca() within function parameters, see the
+ BUGS section of the alloca(3) man page.
+
+- Use memzero() or even better zero() instead of memset(..., 0, ...)
+
+- Instead of using memzero()/memset() to initialize structs allocated
+ on the stack, please try to use c99 structure initializers. It's
+ short, prettier and actually even faster at execution. Hence:
+
+ struct foobar t = {
+ .foo = 7,
+ .bar = "bazz",
+ };
+
+ instead of:
+
+ struct foobar t;
+ zero(t);
+ t.foo = 7;
+ t.bar = "bazz";
+
+- When returning a return code from main(), please preferably use
+ EXIT_FAILURE and EXIT_SUCCESS as defined by libc.
+
+- The order in which header files are included doesn't matter too
+ much. However, please try to include the headers of external
+ libraries first (these are all headers enclosed in <>), followed by
+ the headers of our own public headers (these are all headers
+ starting with "sd-"), internal utility libraries from src/shared/,
+ followed by the headers of the specific component. Or in other
+ words:
+
+ #include <stdio.h>
+ #include "sd-daemon.h"
+ #include "util.h"
+ #include "frobnicator.h"
+
+ Where stdio.h is a public glibc API, sd-daemon.h is a public API of
+ our own, util.h is a utility library header from src/shared, and
+ frobnicator.h is an placeholder name for any systemd component. The
+ benefit of following this ordering is that more local definitions
+ are always defined after more global ones. Thus, our local
+ definitions will never "leak" into the global header files, possibly
+ altering their effect due to #ifdeffery.