1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822
|
LXC Coding Style Guide
======================
In general the LXC project follows the Linux kernel coding style. However,
there are a few differences. They are outlined in this document.
The Linux kernel coding style guide can be found within the kernel tree:
Documentation/process/coding-style.rst
It can be accessed online too:
https://www.kernel.org/doc/html/latest/process/coding-style.html
## 1) General Notes
- The coding style guide refers to new code. But legacy code can be cleaned up
and we are happy to take those patches.
- Just because there is still code in LXC that doesn't adhere to the coding
standards outlined here does not license not adhering to the coding style. In
other words: please stick to the coding style.
- Maintainers are free to ignore rules specified here when merging pull
requests. This guideline might seem a little weird but it exits to ease new
developers into the code base and to prevent unnecessary bikeshedding. If
a maintainer feels hat enforcing a specific rule in a given commit would do
more harm than good they should always feel free to ignore the rule.
Furthermore, when merging pull requests that do not adhere to our coding
style maintainers should feel free to grab the commit, adapt it to our coding
style and add their Signed-off-by line to it. This is especially helpful to
make it easier for first-time contributors and to prevent having pull
requests being stuck in the merge queue because of minor details.
- We currently do not provide automatic coding style checks but if a suitable
tool is found we are happy to integrate it into our test suite. It is
possible and recommended to use the `clang-format` binary to check your code.
The following options are an approximation of the coding style used here.
Simply create a file called `.clang-format` in your home directory with the
following options:
```sh
cat << EOF > "${HOME}"/.clang-format
AlignEscapedNewlines: Left
BreakBeforeBraces: Attach
AlwaysBreakBeforeMultilineStrings: false
BreakBeforeBinaryOperators: None
MaxEmptyLinesToKeep: 1
PenaltyBreakBeforeFirstCallParameter: 1000000
BinPackArguments: true
BinPackParameters: true
AllowAllParametersOfDeclarationOnNextLine: false
AlignAfterOpenBracket: true
SpacesInSquareBrackets: false
SpacesInCStyleCastParentheses: false
SpaceInEmptyParentheses: false
SpaceBeforeParens: ControlStatements
SpaceAfterCStyleCast: false
SortIncludes: true
PenaltyReturnTypeOnItsOwnLine: 10000
PenaltyExcessCharacter: 10
Language: Cpp
ForEachMacros: ['lxc_list_for_each', 'lxc_list_for_each_safe']
AllowShortLoopsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortCaseLabelsOnASingleLine: false
AllowShortBlocksOnASingleLine: false
BasedOnStyle: LLVM
TabWidth: 8
IndentWidth: 8
UseTab: Always
BreakBeforeBraces: Linux
AllowShortIfStatementsOnASingleLine: false
IndentCaseLabels: false
EOF
```
However, it will not handle all cases correctly. For example, most `struct`
initializations will not be correct. In such cases please refer to the coding
style here.
## 2) Only Use Tabs
- LXC uses tabs.
## 3) Only use `/* */` Style Comments
- Any comments that are added must use `/* */`.
- Single-line comments should start on the same line as the opening `/*`.
- Single-line comments should simply be placed between `/* */`. For example:
```C
/* Define pivot_root() if missing from the C library */
```
- Mutli-line comment should start on the next line following the opening
`/*`and should end with the closing `*/` on a separate line. For
example:
```C
/*
* At this point the old-root is mounted on top of our new-root
* To unmounted it we must not be chdir()ed into it, so escape back
* to old-root.
*/
```
## 4) Try To Wrap At 80chars
- This is not strictly enforced. It is perfectly valid to sometimes
overflow this limit if it helps clarity. Nonetheless, try to stick to it
and use common sense to decide when not to.
## 5) Error Messages
- Error messages must start with a capital letter and must **not** end with a
punctuation sign.
- They should be descriptive, without being needlessly long. It is best to just
use already existing error messages as examples.
- The commit message itself is not subject to rule 4), i.e. it should not be
wrapped at 80chars. This is to make it easy to grep for it.
- Examples of acceptable error messages are:
```C
SYSERROR("Failed to create directory \"%s\"", path);
WARN("\"/dev\" directory does not exist. Proceeding without autodev being set up");
```
## 6) Set `errno`
- Functions that can fail in a non-binary way should return `-1` and set
`errno` to a meaningful error code.
As a convenience LXC provides the `minus_one_set_errno` macro:
```C
static int set_config_net_l2proxy(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
{
struct lxc_netdev *netdev = data;
unsigned int val = 0;
int ret;
if (lxc_config_value_empty(value))
return clr_config_net_l2proxy(key, lxc_conf, data);
if (!netdev)
return minus_one_set_errno(EINVAL);
ret = lxc_safe_uint(value, &val);
if (ret < 0)
return minus_one_set_errno(-ret);
switch (val) {
case 0:
netdev->l2proxy = false;
return 0;
case 1:
netdev->l2proxy = true;
return 0;
}
return minus_one_set_errno(EINVAL);
}
```
## 7) All Unexported Functions Must Be Declared `static`
- Functions which are only used in the current file and are not exported
within the codebase need to be declared with the `static` attribute.
## 8) All Exported Functions Must Be Declared `extern` In A Header File
- Functions declared in header files (`*.h`) should use the `extern` keyword.
- Functions declared in source files (`*.c`) should not use the `extern` keyword.
## 9) Declaring Variables
- variables should be declared at the top of the function or at the beginning
of a new scope but **never** in the middle of a scope. They should be ordered
in the following way:
1. automatically freed variables
- This specifically references variables cleaned up via the `cleanup`
attribute as supported by `gcc` and `clang`.
2. initialized variables
3. uninitialized variables
General rules are:
- put base types before complex types
- put standard types defined by libc before types defined by LXC
- put multiple declarations of the same type on the same line
- Examples of good declarations can be seen in the following function:
```C
int lxc_clear_procs(struct lxc_conf *c, const char *key)
{
struct lxc_list *it, *next;
bool all = false;
const char *k = NULL;
if (strcmp(key, "lxc.proc") == 0)
all = true;
else if (strncmp(key, "lxc.proc.", sizeof("lxc.proc.") - 1) == 0)
k = key + sizeof("lxc.proc.") - 1;
else
return -1;
lxc_list_for_each_safe(it, &c->procs, next) {
struct lxc_proc *proc = it->elem;
if (!all && strcmp(proc->filename, k) != 0)
continue;
lxc_list_del(it);
free(proc->filename);
free(proc->value);
free(proc);
free(it);
}
return 0;
}
```
## 10) Functions Not Returning Booleans Must Assign Return Value Before Performing Checks
- When checking whether a function not returning booleans was successful or not
the returned value must be assigned before it is checked (`str{n}cmp()`
functions being one notable exception). For example:
```C
/* assign value to "ret" first */
ret = mount(sourcepath, cgpath, "cgroup", remount_flags, NULL);
/* check whether function was successful */
if (ret < 0) {
SYSERROR("Failed to remount \"%s\" ro", cgpath);
free(sourcepath);
return -1;
}
```
Functions returning booleans can be checked directly. For example:
```C
extern bool lxc_string_in_array(const char *needle, const char **haystack);
/* check right away */
if (lxc_string_in_array("ns", (const char **)h->subsystems))
continue;
```
## 11) Non-Boolean Functions That Behave Like Boolean Functions Must Explicitly Check Against A Value
- This rule mainly exists for `str{n}cmp()` type functions. In most cases they
are used like a boolean function to check whether a string matches or not.
But they return an integer. It is perfectly fine to check `str{n}cmp()`
functions directly but you must compare explicitly against a value. That is
to say, while they are conceptually boolean functions they shouldn't be
treated as such since they don't really behave like boolean functions. So
`if (!str{n}cmp())` and `if (str{n}cmp())` checks must not be used. Good
examples are found in the following functions:
```C
static int set_config_hooks(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
char *copy;
if (lxc_config_value_empty(value))
return lxc_clear_hooks(lxc_conf, key);
if (strcmp(key + 4, "hook") == 0) {
ERROR("lxc.hook must not have a value");
return -1;
}
copy = strdup(value);
if (!copy)
return -1;
if (strcmp(key + 9, "pre-start") == 0)
return add_hook(lxc_conf, LXCHOOK_PRESTART, copy);
else if (strcmp(key + 9, "start-host") == 0)
return add_hook(lxc_conf, LXCHOOK_START_HOST, copy);
else if (strcmp(key + 9, "pre-mount") == 0)
return add_hook(lxc_conf, LXCHOOK_PREMOUNT, copy);
else if (strcmp(key + 9, "autodev") == 0)
return add_hook(lxc_conf, LXCHOOK_AUTODEV, copy);
else if (strcmp(key + 9, "mount") == 0)
return add_hook(lxc_conf, LXCHOOK_MOUNT, copy);
else if (strcmp(key + 9, "start") == 0)
return add_hook(lxc_conf, LXCHOOK_START, copy);
else if (strcmp(key + 9, "stop") == 0)
return add_hook(lxc_conf, LXCHOOK_STOP, copy);
else if (strcmp(key + 9, "post-stop") == 0)
return add_hook(lxc_conf, LXCHOOK_POSTSTOP, copy);
else if (strcmp(key + 9, "clone") == 0)
return add_hook(lxc_conf, LXCHOOK_CLONE, copy);
else if (strcmp(key + 9, "destroy") == 0)
return add_hook(lxc_conf, LXCHOOK_DESTROY, copy);
free(copy);
return -1;
}
```
## 12) Do Not Use C99 Variable Length Arrays (VLA)
- They are made optional and there is no guarantee that future C standards
will support them.
## 13) Use Standard libc Macros When Exiting
- libc provides `EXIT_FAILURE` and `EXIT_SUCCESS`. Use them whenever possible
in the child of `fork()`ed process or when exiting from a `main()` function.
## 14) Use `goto`s
`goto`s are an essential language construct of C and are perfect to perform
cleanup operations or simplify the logic of functions. However, here are the
rules to use them:
- use descriptive `goto` labels.
For example, if you know that this label is only used as an error path you
should use something like `on_error` instead of `out` as label name.
- **only** jump downwards unless you are handling `EAGAIN` errors and want to
avoid `do-while` constructs.
- An example of a good usage of `goto` is:
```C
static int set_config_idmaps(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
{
unsigned long hostid, nsid, range;
char type;
int ret;
struct lxc_list *idmaplist = NULL;
struct id_map *idmap = NULL;
if (lxc_config_value_empty(value))
return lxc_clear_idmaps(lxc_conf);
idmaplist = malloc(sizeof(*idmaplist));
if (!idmaplist)
goto on_error;
idmap = malloc(sizeof(*idmap));
if (!idmap)
goto on_error;
memset(idmap, 0, sizeof(*idmap));
ret = parse_idmaps(value, &type, &nsid, &hostid, &range);
if (ret < 0) {
ERROR("Failed to parse id mappings");
goto on_error;
}
INFO("Read uid map: type %c nsid %lu hostid %lu range %lu", type, nsid, hostid, range);
if (type == 'u')
idmap->idtype = ID_TYPE_UID;
else if (type == 'g')
idmap->idtype = ID_TYPE_GID;
else
goto on_error;
idmap->hostid = hostid;
idmap->nsid = nsid;
idmap->range = range;
idmaplist->elem = idmap;
lxc_list_add_tail(&lxc_conf->id_map, idmaplist);
if (!lxc_conf->root_nsuid_map && idmap->idtype == ID_TYPE_UID)
if (idmap->nsid == 0)
lxc_conf->root_nsuid_map = idmap;
if (!lxc_conf->root_nsgid_map && idmap->idtype == ID_TYPE_GID)
if (idmap->nsid == 0)
lxc_conf->root_nsgid_map = idmap;
idmap = NULL;
return 0;
on_error:
free(idmaplist);
free(idmap);
return -1;
}
```
## 15) Use Booleans instead of integers
- When something can be conceptualized in a binary way use a boolean not
an integer.
## 16) Cleanup Functions Must Handle The Object's Null Type And Being Passed Already Cleaned Up Objects
- If you implement a custom cleanup function to e.g. free a complex type
you declared you must ensure that the object's null type is handled and
treated as a NOOP. For example:
```C
void lxc_free_array(void **array, lxc_free_fn element_free_fn)
{
void **p;
for (p = array; p && *p; p++)
element_free_fn(*p);
free((void*)array);
}
```
- Cleanup functions should also expect to be passed already cleaned up objects.
One way to handle this cleanly is to initialize the cleaned up variable to
a special value that signals the function that the element has already been
freed on the next call. For example, the following function cleans up file
descriptors and sets the already closed file descriptors to `-EBADF`. On the
next call it can simply check whether the file descriptor is positive and
move on if it isn't:
```C
static void lxc_put_attach_clone_payload(struct attach_clone_payload *p)
{
if (p->ipc_socket >= 0) {
shutdown(p->ipc_socket, SHUT_RDWR);
close(p->ipc_socket);
p->ipc_socket = -EBADF;
}
if (p->pty_fd >= 0) {
close(p->pty_fd);
p->pty_fd = -EBADF;
}
if (p->init_ctx) {
lxc_proc_put_context_info(p->init_ctx);
p->init_ctx = NULL;
}
}
```
## 17) Cast to `(void)` When Intentionally Ignoring Return Values
- There are cases where you do not care about the return value of a function.
Please cast the return value to `(void)` when doing so.
- Standard library functions or functions which are known to be ignored by
default do not need to be cast to `(void)`. Classical candidates are
`close()` and `fclose()`.
- A good example is:
```C
for (i = 0; hierarchies[i]; i++) {
char *fullpath;
char *path = hierarchies[i]->fullcgpath;
ret = chowmod(path, destuid, nsgid, 0755);
if (ret < 0)
return -1;
/* failures to chown() these are inconvenient but not
* detrimental we leave these owned by the container launcher,
* so that container root can write to the files to attach. we
* chmod() them 664 so that container systemd can write to the
* files (which systemd in wily insists on doing).
*/
if (hierarchies[i]->version == cgroup_super_magic) {
fullpath = must_make_path(path, "tasks", null);
(void)chowmod(fullpath, destuid, nsgid, 0664);
free(fullpath);
}
fullpath = must_make_path(path, "cgroup.procs", null);
(void)chowmod(fullpath, destuid, 0, 0664);
free(fullpath);
if (hierarchies[i]->version != cgroup2_super_magic)
continue;
fullpath = must_make_path(path, "cgroup.subtree_control", null);
(void)chowmod(fullpath, destuid, nsgid, 0664);
free(fullpath);
fullpath = must_make_path(path, "cgroup.threads", null);
(void)chowmod(fullpath, destuid, nsgid, 0664);
free(fullpath);
}
```
## 18) Use `for (;;)` instead of `while (1)` or `while (true)`
- Let's be honest, it is really the only sensible way to do this.
## 19) Use The Set Of Supported DCO Statements
- Signed-off-by: Random J Developer <random@developer.org>
- You did write this code or have the right to contribute it to LXC.
- Acked-by: Random J Developer <random@developer.org>
- You did read the code and think it is correct. This is usually only used by
maintainers or developers that have made significant contributions and can
vouch for the correctness of someone else's code.
- Reviewed-by: Random J Developer <random@developer.org>
- You did review the code and vouch for its correctness, i.e. you'd be
prepared to fix bugs it might cause. This is usually only used by
maintainers or developers that have made significant contributions and can
vouch for the correctness of someone else's code.
- Co-developed-by: Random J Developer <random@developer.org>
- The code can not be reasonably attributed to a single developer, i.e.
you worked on this together.
- Tested-by: Random J Developer <random@developer.org>
- You verified that the code fixes a given bug or is behaving as advertised.
- Reported-by: Random J Developer <random@developer.org>
- You found and reported the bug.
- Suggested-by: Random J Developer <random@developer.org>
- You wrote the code but someone contributed the idea. This line is usually
overlooked but it is a sign of good etiquette and coding ethics: if someone
helped you solve a problem or had a clever idea do not silently claim it by
slapping your Signed-off-by underneath. Be honest and add a Suggested-by.
## 20) Commit Message Outline
- You **must** stick to the 80chars limit especially in the title of the commit
message.
- Please use English commit messages only.
- use meaningful commit messages.
- Use correct spelling and grammar.
If you are not a native speaker and/or feel yourself struggling with this it
is perfectly fine to point this out and there's no need to apologize. Usually
developers will be happy to pull your branch and adopt the commit message.
- Please always use the affected file (without the file type suffix) or module
as a prefix in the commit message.
- Examples of good commit messages are:
```Diff
commit b87243830e3b5e95fa31a17cf1bfebe55353bf13
Author: Felix Abecassis <fabecassis@nvidia.com>
Date: Fri Feb 2 06:19:13 2018 -0800
hooks: change the semantic of NVIDIA_VISIBLE_DEVICES=""
With LXC, you can override the value of an environment variable to
null, but you can't unset an existing variable.
The NVIDIA hook was previously activated when NVIDIA_VISIBLE_DEVICES
was set to null. As a result, it was not possible to disable the hook
by overriding the environment variable in the configuration.
The hook can now be disabled by setting NVIDIA_VISIBLE_DEVICES to
null or to the new special value "void".
Signed-off-by: Felix Abecassis <fabecassis@nvidia.com>
commit d6337a5f9dc7311af168aa3d586fdf239f5a10d3
Author: Christian Brauner <christian.brauner@ubuntu.com>
Date: Wed Jan 31 16:25:11 2018 +0100
cgroups: get controllers on the unified hierarchy
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
```
## 21) Use `_exit()` To Terminate `fork()`ed Child Processes
- When `fork()`ing off a child process use `_exit()` to terminate it instead of
`exit()`. The `exit()` function is not thread-safe and thus not suited for
the shared library which must ensure that it is thread-safe.
## 22) Keep Arrays of `struct`s Aligned Horizontally When Initializing
- Arrays of `struct`s are:
```C
struct foo_struct {
int n;
int m;
int p;
};
struct foo_struct new_instance[] = {
{ 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9 },
};
```
- Leave a single space after the opening `{` and before closing `}` of the
largest member of the last column.
- Always leave a single space between the largest member of the current column
and the member in the next column.
- A good example is
```C
struct signame {
int num;
const char *name;
};
static const struct signame signames[] = {
{ SIGHUP, "HUP" },
{ SIGINT, "INT" },
{ SIGQUIT, "QUIT" },
{ SIGILL, "ILL" },
{ SIGABRT, "ABRT" },
{ SIGFPE, "FPE" },
{ SIGKILL, "KILL" },
{ SIGSEGV, "SEGV" },
{ SIGPIPE, "PIPE" },
{ SIGALRM, "ALRM" },
{ SIGTERM, "TERM" },
{ SIGUSR1, "USR1" },
{ SIGUSR2, "USR2" },
{ SIGCHLD, "CHLD" },
{ SIGCONT, "CONT" },
{ SIGSTOP, "STOP" },
{ SIGTSTP, "TSTP" },
{ SIGTTIN, "TTIN" },
{ SIGTTOU, "TTOU" },
#ifdef SIGTRAP
{ SIGTRAP, "TRAP" },
#endif
#ifdef SIGIOT
{ SIGIOT, "IOT" },
#endif
#ifdef SIGEMT
{ SIGEMT, "EMT" },
#endif
#ifdef SIGBUS
{ SIGBUS, "BUS" },
#endif
#ifdef SIGSTKFLT
{ SIGSTKFLT, "STKFLT" },
#endif
#ifdef SIGCLD
{ SIGCLD, "CLD" },
#endif
#ifdef SIGURG
{ SIGURG, "URG" },
#endif
#ifdef SIGXCPU
{ SIGXCPU, "XCPU" },
#endif
#ifdef SIGXFSZ
{ SIGXFSZ, "XFSZ" },
#endif
#ifdef SIGVTALRM
{ SIGVTALRM, "VTALRM" },
#endif
#ifdef SIGPROF
{ SIGPROF, "PROF" },
#endif
#ifdef SIGWINCH
{ SIGWINCH, "WINCH" },
#endif
#ifdef SIGIO
{ SIGIO, "IO" },
#endif
#ifdef SIGPOLL
{ SIGPOLL, "POLL" },
#endif
#ifdef SIGINFO
{ SIGINFO, "INFO" },
#endif
#ifdef SIGLOST
{ SIGLOST, "LOST" },
#endif
#ifdef SIGPWR
{ SIGPWR, "PWR" },
#endif
#ifdef SIGUNUSED
{ SIGUNUSED, "UNUSED" },
#endif
#ifdef SIGSYS
{ SIGSYS, "SYS" },
#endif
};
```
## 23) Use `strlcpy()` instead of `strncpy()`
When copying strings always use `strlcpy()` instead of `strncpy()`. The
advantage of `strlcpy()` is that it will always append a `\0` byte to the
string.
Unless you have a valid reason to accept truncation you must check whether
truncation has occurred, treat it as an error, and handle the error
appropriately.
## 24) Use `strlcat()` instead of `strncat()`
When concatenating strings always use `strlcat()` instead of `strncat()`. The
advantage of `strlcat()` is that it will always append a `\0` byte to the
string.
Unless you have a valid reason to accept truncation you must check whether
truncation has occurred, treat it as an error, and handle the error
appropriately.
## 25) Use `__fallthrough__` in switch statements
If LXC detects that the compiler is new enough it will tell it to check
`switch` statements for non-documented fallthroughs. Please always place
a `__fallthrough__` after a `case` which falls through the next one.
```c
int lxc_attach_run_command(void *payload)
{
int ret = -1;
lxc_attach_command_t *cmd = payload;
ret = execvp(cmd->program, cmd->argv);
if (ret < 0) {
switch (errno) {
case ENOEXEC:
ret = 126;
break;
case ENOTDIR:
__fallthrough;
case ENOENT:
ret = 127;
break;
}
}
SYSERROR("Failed to exec \"%s\"", cmd->program);
return ret;
}
```
## 24) Never use `fgets()`
LXC does not allow the use of `fgets()`. Use `getline()` or other methods
instead.
## 25) Never allocate memory on the stack
This specifically forbids any usage of `alloca()` in the codebase.
## 26) Use cleanup macros supported by `gcc` and `clang`
LXC has switched from manually cleaning up resources to using cleanup macros
supported by `gcc` and `clang`:
```c
__attribute__((__cleanup__(<my-cleanup-function-wrapper>)))
```
We do not allow manually cleanups anymore if there are appropriate macros.
Currently the following macros are supported:
```c
/* close file descriptor */
__do_close_prot_errno
/* free allocated memory */
__do_free __attribute__((__cleanup__(__auto_free__)))
/* close FILEs */
__do_fclose __attribute__((__cleanup__(__auto_fclose__)))
/* close DIRs */
__do_closedir __attribute__((__cleanup__(__auto_closedir__)))
```
For example:
```c
void turn_into_dependent_mounts(void)
{
__do_free char *line = NULL;
__do_fclose FILE *f = NULL;
__do_close int memfd = -EBADF, mntinfo_fd = -EBADF;
int ret;
ssize_t copied;
size_t len = 0;
mntinfo_fd = open("/proc/self/mountinfo", O_RDONLY | O_CLOEXEC);
if (mntinfo_fd < 0) {
SYSERROR("Failed to open \"/proc/self/mountinfo\"");
return;
}
memfd = memfd_create(".lxc_mountinfo", MFD_CLOEXEC);
if (memfd < 0) {
char template[] = P_tmpdir "/.lxc_mountinfo_XXXXXX";
if (errno != ENOSYS) {
SYSERROR("Failed to create temporary in-memory file");
return;
}
memfd = lxc_make_tmpfile(template, true);
if (memfd < 0) {
WARN("Failed to create temporary file");
return;
}
}
again:
copied = lxc_sendfile_nointr(memfd, mntinfo_fd, NULL, LXC_SENDFILE_MAX);
if (copied < 0) {
if (errno == EINTR)
goto again;
SYSERROR("Failed to copy \"/proc/self/mountinfo\"");
return;
}
ret = lseek(memfd, 0, SEEK_SET);
if (ret < 0) {
SYSERROR("Failed to reset file descriptor offset");
return;
}
f = fdopen(memfd, "re");
if (!f) {
SYSERROR("Failed to open copy of \"/proc/self/mountinfo\" to mark all shared. Continuing");
return;
}
/*
* After a successful fdopen() memfd will be closed when calling
* fclose(f). Calling close(memfd) afterwards is undefined.
*/
move_fd(memfd);
while (getline(&line, &len, f) != -1) {
char *opts, *target;
target = get_field(line, 4);
if (!target)
continue;
opts = get_field(target, 2);
if (!opts)
continue;
null_endofword(opts);
if (!strstr(opts, "shared"))
continue;
null_endofword(target);
ret = mount(NULL, target, NULL, MS_SLAVE, NULL);
if (ret < 0) {
SYSERROR("Failed to recursively turn old root mount tree into dependent mount. Continuing...");
continue;
}
TRACE("Recursively turned old root mount tree into dependent mount");
}
TRACE("Turned all mount table entries into dependent mount");
}
```
|