X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=CODING_STYLE;h=ed61ea9d28169d4d4f9fffbc9f1898314c4e6cb9;hp=9341b48d4103a19b6b75c206c58c508a60e8a5ec;hb=91d60274701a12d2bbcd2b8e40f8b8abe00be0e7;hpb=35b8ca3aaf8cb044ad76675dfcad89e000dd4a5c diff --git a/CODING_STYLE b/CODING_STYLE index 9341b48d4..ed61ea9d2 100644 --- a/CODING_STYLE +++ b/CODING_STYLE @@ -1,27 +1,436 @@ +- 8ch indent, no tabs, except for files in man/ which are 2ch indent, + and still no tabs -- 8ch indent, 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!) -- structs in MixedCase, variables, functions in lower_case +- 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 + then again, don't overdo it, ~119ch should be enough really. -- the destructors always unregister the object from the next bigger +- Variables and functions *must* be static, unless they have a + prototype, and are supposed to be exported. + +- structs in MixedCase (with exceptions, such as public API structs), + variables + functions in lower_case. + +- The destructors always unregister the object from the next bigger object, not the other way around -- to minimize strict aliasing violations we prefer unions over casting +- To minimize strict aliasing violations, we prefer unions over casting -- for robustness reasons destructors should be able to destruct +- 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 - are some exceptions: for constructors its is OK to return NULL on - OOM. For lookup functions NULL is fine too for "not found". +- 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". + + Be strict with this. When you write a function that can fail due to + more than one cause, it *really* should have "int" as return value + for the error code. + +- Do not bother with error checking whether writing to stdout/stderr + worked. + +- Do not log errors from "library" code, only do so from "main + program" code. (With one exception: it is OK to log with DEBUG level + from any code, with the exception of maybe inner loops). + +- Always check OOM. There is no excuse. In program code, you can use + "log_oom()" for then printing a short message, but not in "library" code. - Do not issue NSS requests (that includes user name and host name - lookups) from the main daemon as this might trigger deadlocks when - we those lookups involve synchronously talking to services that we - would need to start up. + lookups) from PID 1 as this might trigger deadlocks when those + lookups involve synchronously talking to services that we would need + to start up + +- Do not synchronously talk to any other service from PID 1, due to + risk of deadlocks + +- Avoid fixed-size string buffers, unless you really know the maximum + size and that maximum size is small. They are a source of errors, + since they possibly result in truncated strings. It is often nicer + to use dynamic memory, alloca() or VLAs. If you do allocate fixed-size + strings on the stack, then it is probably only OK if you either + use a maximum size such as LINE_MAX, or count in detail the maximum + size a string can have. (DECIMAL_STR_MAX and DECIMAL_STR_WIDTH + macros are your friends for this!) + + Or in other words, if you use "char buf[256]" then you are likely + doing something wrong! + +- Stay uniform. For example, always use "usec_t" for time + 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! + +- Be exceptionally careful when formatting and parsing floating point + numbers. Their syntax is locale dependent (i.e. "5.000" in en_US is + generally understood as 5, while on de_DE as 5000.). + +- Try to use this: + + void foo() { + } + + instead of this: + + void foo() + { + } + + 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. + +- Please do not allocate variables on the stack in the middle of code, + even if C99 allows it. Wrong: + + { + a = 5; + int b; + b = a; + } + + Right: + + { + int b; + a = 5; + b = a; + } + +- Unless you allocate an array, "double" is always the better choice + than "float". Processors speak "double" natively anyway, so this is + no speed benefit, and on calls like printf() "float"s get promoted + to "double"s anyway, so there is no point. + +- Do not mix function invocations with variable definitions in one + line. Wrong: + + { + int a = foobar(); + uint64_t x = 7; + } + + Right: + + { + int a; + uint64_t x = 7; + + a = foobar(); + } + +- Use "goto" for cleaning up, and only use it for that. i.e. you may + only jump to the end of a function, and little else. Never jump + backwards! + +- Think about the types you use. If a value cannot sensibly be + negative, do not use "int", but use "unsigned". + +- Use "char" only for actual characters. Use "uint8_t" or "int8_t" + when you actually mean a byte-sized signed or unsigned + integers. When referring to a generic byte, we generally prefer the + unsigned variant "uint8_t". Do not use types based on "short". They + *never* make sense. Use ints, longs, long longs, all in + unsigned+signed fashion, and the fixed size types + uint8_t/uint16_t/uint32_t/uint64_t/int8_t/int16_t/int32_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 + other functions should be prefixed like that. + +- In public API calls, you *must* validate all your input arguments for + programming error with assert_return() and return a sensible return + code. In all other calls, it is recommended to check for programming + errors with a more brutal assert(). We are more forgiving to public + users than for ourselves! Note that assert() and assert_return() + really only should be used for detecting programming errors, not for + runtime errors. assert() and assert_return() by usage of _likely_() + inform the compiler that he should not expect these checks to fail, + and they inform fellow programmers about the expected validity and + range of parameters. + +- Never use strtol(), atoi() and similar calls. Use safe_atoli(), + safe_atou32() and suchlike instead. They are much nicer to use in + most cases and correctly check for parsing errors. + +- For every function you add, think about whether it is a "logging" + function or a "non-logging" function. "Logging" functions do logging + on their own, "non-logging" function never log on their own and + expect their callers to log. All functions in "library" code, + i.e. in src/shared/ and suchlike must be "non-logging". Every time a + "logging" function calls a "non-logging" function, it should log + about the resulting errors. If a "logging" function calls another + "logging" function, then it should not generate log messages, so + that log messages are not generated twice for the same errors. + +- Avoid static variables, except for caches and very few other + cases. Think about thread-safety! While most of our code is never + used in threaded environments, at least the library code should make + sure it works correctly in them. Instead of doing a lot of locking + for that, we tend to prefer using TLS to do per-thread caching (which + only works for small, fixed-size cache objects), or we disable + caching for any thread that is not the main thread. Use + is_main_thread() to detect whether the calling thread is the main + thread. + +- 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 + f) invocations of fopen() should take "e" + +- 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 cast function calls to (void) that return no error + conditions. Specifically, the various xyz_unref() calls that return a NULL + object shouldn't be cast to (void), since not using the return value does not + hide any errors. + +- 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. systemd-internal headers must not rely on an include order, so + it is safe to include them in any order possible. + However, to not clutter global includes, and to make sure internal + definitions will not affect global headers, please always include the + headers of external components first (these are all headers enclosed + in <>), followed by our own exported headers (usually everything + that's prefixed by "sd-"), and then followed by internal headers. + Furthermore, in all three groups, order all includes alphabetically + so duplicate includes can easily be detected. + +- To implement an endless loop, use "for (;;)" rather than "while + (1)". The latter is a bit ugly anyway, since you probably really + meant "while (true)"... To avoid the discussion what the right + always-true expression for an infinite while() loop is our + recommendation is to simply write it without any such expression by + using "for (;;)". + +- Never use the "off_t" type, and particularly avoid it in public + APIs. It's really weirdly defined, as it usually is 64bit and we + don't support it any other way, but it could in theory also be + 32bit. Which one it is depends on a compiler switch chosen by the + compiled program, which hence corrupts APIs using it unless they can + also follow the program's choice. Moreover, in systemd we should + parse values the same way on all architectures and cannot expose + off_t values over D-Bus. To avoid any confusion regarding conversion + and ABIs, always use simply uint64_t directly. + +- Commit message subject lines should be prefixed with an appropriate + component name of some kind. For example "journal: ", "nspawn: " and + so on. + +- Do not use "Signed-Off-By:" in your commit messages. That's a kernel + thing we don't do in the systemd project. + +- Avoid leaving long-running child processes around, i.e. fork()s that + are not followed quickly by an execv() in the child. Resource + management is unclear in this case, and memory CoW will result in + unexpected penalties in the parent much much later on. + +- Don't block execution for arbitrary amounts of time using usleep() + or a similar call, unless you really know what you do. Just "giving + something some time", or so is a lazy excuse. Always wait for the + proper event, instead of doing time-based poll loops. + +- To determine the length of a constant string "foo", don't bother + with sizeof("foo")-1, please use strlen("foo") directly. gcc knows + strlen() anyway and turns it into a constant expression if possible. + +- If you want to concatenate two or more strings, consider using + strjoin() rather than asprintf(), as the latter is a lot + slower. This matters particularly in inner loops. + +- Please avoid using global variables as much as you can. And if you + do use them make sure they are static at least, instead of + exported. Especially in library-like code it is important to avoid + global variables. Why are global variables bad? They usually hinder + generic reusability of code (since they break in threaded programs, + and usually would require locking there), and as the code using them + has side-effects make programs intransparent. That said, there are + many cases where they explicitly make a lot of sense, and are OK to + use. For example, the log level and target in log.c is stored in a + global variable, and that's OK and probably expected by most. Also + in many cases we cache data in global variables. If you add more + caches like this, please be careful however, and think about + threading. Only use static variables if you are sure that + thread-safety doesn't matter in your case. Alternatively consider + using TLS, which is pretty easy to use with gcc's "thread_local" + concept. It's also OK to store data that is inherently global in + global variables, for example data parsed from command lines, see + below. + +- If you parse a command line, and want to store the parsed parameters + in global variables, please consider prefixing their names with + "arg_". We have been following this naming rule in most of our + tools, and we should continue to do so, as it makes it easy to + identify command line parameter variables, and makes it clear why it + is OK that they are global variables. + +- When exposing public C APIs, be careful what function parameters you make + "const". For example, a parameter taking a context object should probably not + be "const", even if you are writing an other-wise read-only accessor function + for it. The reason is that making it "const" fixates the contract that your + call won't alter the object ever, as part of the API. However, that's often + quite a promise, given that this even prohibits object-internal caching or + lazy initialization of object variables. Moreover it's usually not too useful + for client applications. Hence: please be careful and avoid "const" on object + parameters, unless you are very sure "const" is appropriate. + +- Make sure to enforce limits on every user controllable resource. If the user + can allocate resources in your code, your code must enforce some form of + limits after which it will refuse operation. It's fine if it is hardcoded (at + least initially), but it needs to be there. This is particularly important + for objects that unprivileged users may allocate, but also matters for + everything else any user may allocated. + +- htonl()/ntohl() and htons()/ntohs() are weird. Please use htobe32() and + htobe16() instead, it's much more descriptive, and actually says what really + is happening, after all htonl() and htons() don't operation on longs and + shorts as their name would suggest, but on uint32_t and uint16_t. Also, + "network byte order" is just a weird name for "big endian", hence we might + want to call it "big endian" right-away. + +- You might wonder what kind of common code belongs in src/shared/ and what + belongs in src/basic/. The split is like this: anything that uses public APIs + we expose (i.e. any of the sd-bus, sd-login, sd-id128, ... APIs) must be + located in src/shared/. All stuff that only uses external libraries from + other projects (such as glibc's APIs), or APIs from src/basic/ itself should + be placed in src/basic/. Conversely, src/libsystemd/ may only use symbols + from src/basic, but not from src/shared/. To summarize: + + src/basic/ → may be used by all code in the tree + → may not use any code outside of src/basic/ + + src/libsystemd/ → may be used by all code in the tree, except for code in src/basic/ + → may not use any code outside of src/basic/, src/libsystemd/ + + src/shared/ → may be used by all code in the tree, except for code in src/basic/, src/libsystemd/ + → may not use any code outside of src/basic/, src/libsystemd/, src/shared/ -- Do not access any directories outside of /etc/, /dev, /lib from the - init daemon to avoid deadlocks with the automounter. +- Our focus is on the GNU libc (glibc), not any other libcs. If other libcs are + incompatible with glibc it's on them. However, if there are equivalent POSIX + and Linux/GNU-specific APIs, we generally prefer the POSIX APIs. If there + aren't, we are happy to use GNU or Linux APIs, and expect non-GNU + implementations of libc to catch up with glibc. -- Don't synchronously talk to any other service, due to risk of - deadlocks. +- Whenever installing a signal handler, make sure to set SA_RESTART for it, so + that interrupted system calls are automatically restarted, and we minimize + hassles with handling EINTR (in particular as EINTR handling is pretty broken + on Linux).