/ meson.build
meson.build
  1  # SPDX-FileCopyrightText: Copyright (C) 2024-2025 Marek Küthe <m.k@mk16.de>
  2  #
  3  # SPDX-License-Identifier: GPL-3.0-or-later
  4  
  5  project(
  6      'crazytrace',
  7      'cpp',
  8      version: '1.0.7',
  9      license: ['GPL-3.0-or-later'],
 10      default_options: [
 11          'buildtype=release',
 12          'optimization=2',
 13          'warning_level=3',
 14          'werror=true',
 15          'cpp_std=gnu++26',
 16          'b_asneeded=true',
 17          'b_lto=true',
 18          'b_ndebug=if-release',
 19          'b_pie=true'
 20      ],
 21      meson_version: '>=1.5.0'
 22  )
 23  
 24  cpp_compiler = meson.get_compiler('cpp')
 25  
 26  main_file = files('src/main.cpp')
 27  sources = files(
 28      'src/capability_managment.cpp',
 29      'src/configuration.cpp',
 30      'src/crazytrace.cpp',
 31      'src/landlock.cpp',
 32      'src/loglevel.cpp',
 33      'src/posix_wrapper.cpp',
 34      'src/postup_commands.cpp',
 35      'src/nodecontainer.cpp',
 36      'src/nodeinfo.cpp',
 37      'src/nodereply.cpp',
 38      'src/noderequest.cpp',
 39      'src/randomgenerator.cpp',
 40      'src/seccomp.cpp'
 41  )
 42  headers = files(
 43      'src/capability_managment.hpp',
 44      'src/capsicum.hpp',
 45      'src/configuration.hpp',
 46      'src/crazytrace.hpp',
 47      'src/deviceclient.hpp',
 48      'src/ip_ranges.hpp',
 49      'src/landlock.hpp',
 50      'src/loglevel.hpp',
 51      'src/posix_wrapper.hpp',
 52      'src/postup_commands.hpp',
 53      'src/nodecontainer.hpp',
 54      'src/nodeinfo.hpp',
 55      'src/nodereply.hpp',
 56      'src/noderequest.hpp',
 57      'src/randomgenerator.hpp',
 58      'src/seccomp.hpp'
 59  )
 60  tests_sources = files(
 61      'tests/loglevel_test.cpp',
 62      'tests/nodecontainer_test.cpp',
 63      'tests/nodeinfo_test.cpp',
 64      'tests/nodeinfo_children_test.cpp',
 65      'tests/nodereply_test.cpp',
 66      'tests/noderequest_test.cpp',
 67      'tests/randomgenerator_test.cpp'
 68  )
 69  markdown_files = files(
 70      'BUILDING.md',
 71      'CHANGELOG.md',
 72      'CONTRIBUTING.md',
 73      'README.md',
 74      'SECURITY.md'
 75  )
 76  all_sources = main_file + sources + headers + tests_sources
 77  
 78  if get_option('native')
 79      native_flags = ['-march=native', '-mtune=native']
 80  
 81      add_project_arguments(cpp_compiler.get_supported_arguments(native_flags), language: 'cpp')
 82  endif
 83  
 84  # see https://best.openssf.org/Compiler-Hardening-Guides/Compiler-Options-Hardening-Guide-for-C-and-C++.html
 85  hardening_compiler_flags = [
 86      '-Wformat=2', # Strict printf/scanf format checks
 87      '-Wconversion', # Warnings for potentially lossy implicit conversions
 88      '-Wsign-conversion',
 89      '-Wtrampolines', # Warn about stack trampolines (conflicts with non‑executable stack)
 90      '-Wbidi-chars=any', # Enable warnings for possibly misleading Unicode bidirectional control characters
 91      '-fstrict-flex-arrays=3', # Flexible arrays can only be declared with [].
 92      '-fcf-protection=full', # Prevent certain attacks by enabling additional protection against unexpected control flow jumps. Minimal performance loss, as often implemented on the hardware side.
 93      '-mbranch-protection=standard'
 94  ]
 95  add_project_arguments(cpp_compiler.get_supported_arguments(hardening_compiler_flags), language: 'cpp')
 96  
 97  hardening_linker_flags = [
 98      '-Wl,-z,noexecstack', # Mark the stack in memory as non-executable
 99      '-Wl,-z,nodlopen', # Prevent loading this code via dlopen()
100      '-Wl,-z,relro', # Make relocation data read‑only after startup
101      '-Wl,-z,now' # Force the resolution of symbols from dynamic libraries at program startup
102  ]
103  add_project_link_arguments(cpp_compiler.get_supported_link_arguments(hardening_linker_flags), language: 'cpp')
104  
105  if get_option('debug')
106      # see https://sourceware.org/bugzilla/show_bug.cgi?id=27375
107      add_project_arguments(cpp_compiler.get_supported_arguments(['-gdwarf-4']), language: 'cpp')
108  endif
109  
110  if host_machine.system() == 'freebsd'
111      # see https://github.com/exult/exult/commit/202264bcf37d4edc8eafdaf53e1a910c8cf181a7
112      add_project_arguments('-isystem/usr/local/include', language: 'cpp')
113  endif
114  
115  cpp_compiler.has_header('unistd.h', required: true)
116  cpp_compiler.has_function('dup', prefix: '#include <unistd.h>', required: true)
117  
118  if get_option('enable_setugid')
119      cpp_compiler.has_function('setuid', prefix: '#include <unistd.h>', required: true)
120      cpp_compiler.has_function('setgid', prefix: '#include <unistd.h>', required: true)
121      cpp_compiler.has_type('struct passwd', prefix: '#include <pwd.h>', required: true)
122      cpp_compiler.has_type('struct group', prefix: '#include <grp.h>', required: true)
123      add_project_arguments('-DHAVE_SETUGID', language: 'cpp')
124  endif
125  
126  libtuntappp_dep = dependency('libtuntap++', required: false, include_type: 'system')
127  if not libtuntappp_dep.found()
128      libtuntappp_dep = cpp_compiler.find_library('libtuntap++', has_headers: ['tuntap++.hh'], required: false)
129      if not libtuntappp_dep.found()
130          libtuntappp_dep = cpp_compiler.find_library('tuntap++', has_headers: ['tuntap++.hh'], required: true)
131      endif
132  endif
133  
134  libtuntap_dep = dependency('libtuntap', required: false, include_type: 'system')
135  if not libtuntap_dep.found()
136      libtuntap_dep = cpp_compiler.find_library('libtuntap', has_headers: ['tuntap.h'], required: false)
137      if not libtuntap_dep.found()
138          libtuntap_dep = cpp_compiler.find_library('tuntap', has_headers: ['tuntap.h'], required: true)
139      endif
140  endif
141  
142  # Workaround for https://github.com/boostorg/process/issues/494#issuecomment-3392312248
143  boost_dep = dependency('boost', modules: ['log', 'process', 'filesystem'], required: false, include_type: 'system')
144  if not boost_dep.found()
145      boost_dep = dependency('boost', modules: ['log'], include_type: 'system')
146      add_project_arguments('-DBOOST_PROCESS_V1', language: 'cpp')
147  endif
148  
149  libcapng_dep = dependency('libcap-ng', required: false)
150  if libcapng_dep.found()
151      add_project_arguments('-DHAVE_LIBCAPNG', language: 'cpp')
152  endif
153  
154  seccomp_dep = dependency('libseccomp', required: false)
155  if seccomp_dep.found() and cpp_compiler.has_header('sys/syscall.h')
156      add_project_arguments('-DHAVE_SECCOMP', language: 'cpp')
157  endif
158  
159  if cpp_compiler.has_header('linux/landlock.h')
160      add_project_arguments('-DHAVE_LANDLOCK', language: 'cpp')
161  
162      landlock_functions = [
163          'landlock_create_ruleset',
164          'landlock_add_rule',
165          'landlock_restrict_self',
166      ]
167  
168      foreach function : landlock_functions
169          if not cpp_compiler.has_function(function, prefix: '#include <linux/landlock.h>')
170              cpp_compiler.has_header('unistd.h', required: true)
171              cpp_compiler.has_function('syscall', prefix: '#include <unistd.h>', required: true)
172              cpp_compiler.has_header('sys/syscall.h', required: true)
173              syscall_number_macro = 'SYS_' + function
174              cpp_compiler.compiles('#include <sys/syscall.h>\nint number = ' + syscall_number_macro + ';', name: syscall_number_macro + ' number is available', required: true)
175  
176              macro = 'NO_' + function.to_upper()
177              add_project_arguments('-D' + macro, language: 'cpp')
178          endif
179      endforeach
180  
181      # see also https://lore.kernel.org/landlock/20251119212707.71275873@ciel/T/
182      cpp_compiler.compiles(
183          '''
184              #include <linux/landlock.h>
185              struct landlock_ruleset_attr attr = {
186                  .handled_access_fs = 0,
187                  .handled_access_net = 0,
188                  .scoped = 0
189              };
190          ''',
191          name: 'Landlock supports scoped access control',
192          required: true
193      )
194  
195      cpp_compiler.compiles(
196          '''
197              #include <linux/landlock.h>
198              struct landlock_path_beneath_attr attr = {
199                  .allowed_access = 0,
200                  .parent_fd = 0
201              };
202          ''',
203          name: 'Landlock has landlock_path_beneath_attr',
204          required: true
205      )
206  
207      cpp_compiler.compiles(
208          '''
209              #include <linux/landlock.h>
210              struct landlock_net_port_attr attr = {
211                  .allowed_access = 0,
212                  .port = 0
213              };
214          ''',
215          name: 'Landlock has landlock_net_port_attr',
216          required: true
217      )
218  
219      landlock_macros = [
220          'LANDLOCK_RULE_PATH_BENEATH',
221          'LANDLOCK_RULE_NET_PORT',
222  
223          'LANDLOCK_CREATE_RULESET_VERSION',
224  
225          'LANDLOCK_ACCESS_FS_EXECUTE',
226          'LANDLOCK_ACCESS_FS_WRITE_FILE',
227          'LANDLOCK_ACCESS_FS_READ_FILE',
228          'LANDLOCK_ACCESS_FS_TRUNCATE',
229          'LANDLOCK_ACCESS_FS_READ_DIR',
230          'LANDLOCK_ACCESS_FS_REMOVE_DIR',
231          'LANDLOCK_ACCESS_FS_REMOVE_FILE',
232          'LANDLOCK_ACCESS_FS_MAKE_CHAR',
233          'LANDLOCK_ACCESS_FS_MAKE_DIR',
234          'LANDLOCK_ACCESS_FS_MAKE_REG',
235          'LANDLOCK_ACCESS_FS_MAKE_SOCK',
236          'LANDLOCK_ACCESS_FS_MAKE_FIFO',
237          'LANDLOCK_ACCESS_FS_MAKE_BLOCK',
238          'LANDLOCK_ACCESS_FS_MAKE_SYM',
239          'LANDLOCK_ACCESS_FS_REFER',
240          'LANDLOCK_ACCESS_FS_IOCTL_DEV',
241  
242          'LANDLOCK_ACCESS_NET_BIND_TCP',
243          'LANDLOCK_ACCESS_NET_CONNECT_TCP',
244  
245          'LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET',
246          'LANDLOCK_SCOPE_SIGNAL'
247      ]
248  
249      foreach macro : landlock_macros
250          cpp_compiler.compiles('#include <linux/landlock.h>\nint number = ' + macro + ';', name: macro + ' number is available', required: true)
251      endforeach
252  
253      if cpp_compiler.compiles('#include <linux/landlock.h>\nint number = LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON;', name: 'LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON number is available')
254          add_project_arguments('-DHAVE_LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON', language: 'cpp')
255      endif
256  endif
257  
258  if cpp_compiler.has_header('capsicum_helpers.h') and cpp_compiler.has_header('sys/capsicum.h')
259      add_project_arguments('-DHAVE_CAPSICUM', language: 'cpp')
260  endif
261  
262  threads_dep = declare_dependency()
263  if host_machine.system() == 'freebsd'
264      # see https://github.com/exult/exult/issues/436
265      #     https://github.com/exult/exult/blob/38e8b8d140102ea9ec3632f618af7e73a43955df/configure.ac
266      threads_dep = dependency('threads')
267  endif
268  
269  libtins_dep = dependency('libtins', include_type: 'system')
270  yamlcpp_dep = dependency('yaml-cpp', include_type: 'system')
271  gtest_dep = dependency('gtest', main: true, required: false, include_type: 'system')
272  
273  cppcheck = find_program('cppcheck', required: false)
274  if cppcheck.found()
275      run_target('cppcheck', command: [
276          cppcheck,
277          # Do not check external libraries
278          '--suppress=*:*/tins/*',
279          '--suppress=*:*/boost/*',
280          '--suppress=*:*/gtest/*',
281  
282          # Problems with detecting system libraries
283          '--suppress=missingIncludeSystem',
284          '--suppress=unmatchedSuppression',
285          '--suppress=functionStatic',
286  
287          '--enable=warning,style,information,missingInclude',
288          '--error-exitcode=1',
289          '--check-level=exhaustive',
290          '--inline-suppr',
291          '--project=' + join_paths(meson.current_build_dir(), 'compile_commands.json')
292      ])
293  endif
294  
295  infer = find_program('infer', required: false)
296  if infer.found()
297      run_target('infer', command: [
298          infer,
299          '--compilation-database',
300          join_paths(meson.current_build_dir(), 'compile_commands.json'),
301          '--fail-on-issue'
302      ])
303  endif
304  
305  flawfinder = find_program('flawfinder', required: false)
306  if flawfinder.found()
307      run_target('flawfinder', command: [
308          flawfinder,
309          '--error-level=0',
310          all_sources
311      ])
312  endif
313  
314  lizard = find_program('lizard', required: false)
315  if lizard.found()
316      run_target('lizard', command: [
317          lizard,
318          '--CCN', '25',
319          '--length', '400',
320          '--arguments', '10',
321          all_sources
322      ])
323  endif
324  
325  mdl = find_program('mdl', required: false)
326  if mdl.found()
327      run_target('mdl', command: [
328          mdl,
329          markdown_files
330      ])
331  endif
332  
333  reuse = find_program('reuse', required: false)
334  if reuse.found()
335      run_target('reuse-annotate', command: [
336          reuse,
337          'annotate',
338          '--copyright', 'Marek Küthe <m.k@mk16.de>',
339          '--license', 'GPL-3.0-or-later',
340          '--year', '2025',
341          '--copyright-prefix', 'spdx-string-c',
342          '--merge-copyrights',
343          '--recursive',
344          '--skip-unrecognised',
345          meson.current_source_dir()
346      ])
347      run_target('reuse-download', command: [
348          reuse,
349          'download',
350          '--all'
351      ])
352      run_target('reuse-lint', command: [
353          reuse,
354          'lint'
355      ])
356  endif
357  
358  if get_option('install_systemd_unit')
359      install_data(
360          'systemd/crazytrace@.service',
361          install_dir: 'lib/systemd/system'
362      )
363  endif
364  
365  if get_option('install_rc_script')
366      install_data(
367          'rc/crazytrace',
368          install_dir: 'etc/rc.d'
369      )
370  endif
371  
372  if get_option('install_apparmor_profile')
373      install_data(
374          'apparmor/usr.bin.crazytrace',
375          install_dir: '/etc/apparmor.d'
376      )
377  endif
378  
379  if get_option('install_documentation')
380      documentation_files = [
381          'BUILDING.md',
382          'CHANGELOG.md',
383          'CONTRIBUTING.md',
384          'README.md',
385          'SECURITY.md',
386          'LICENSE.txt'
387      ]
388  
389      foreach file : documentation_files
390          install_data(
391              file,
392              install_dir: 'share/doc/crazytrace'
393          )
394      endforeach
395  endif
396  
397  deps = [libtuntappp_dep, libtuntap_dep, boost_dep, libtins_dep, yamlcpp_dep, libcapng_dep, seccomp_dep, threads_dep]
398  
399  if gtest_dep.found()
400      gtest = executable('gtest', sources + tests_sources, dependencies: [gtest_dep] + deps, include_directories: ['src/'])
401      test('gtest', gtest)
402  endif
403  
404  executable(
405      'crazytrace',
406      sources + main_file,
407      dependencies: deps,
408      install : true
409  )