Linux uname() per FFI in dart/flutter einbinden

uname() unter dart zur Bestimmung der CPU Architektur per FFI einbinden.

Linux uname() per FFI in dart/flutter einbinden
Raspberry PI Zero 2 W mit Shield - Grove Base HAT

Mit Pi OS 64 wurde das Paket system_info, das in meinem Projekt dart_periphery die CPU Archtitektur des SoC bestimmt hat, dysfunktional. system_info hat für die Bestimmung der CPU Architektur die relevanten Elemente des Device-Tree /proc/cpuinfo ausgelesen. Im Gegensatz zur 32-Bit Version liefert aber die 64-Bit Version des Device-Trees diese benötige Information nicht mehr zurück. Abgesehen von diesem technischen Problem, hat das Paket den Status discontinued erlangt. Als Ersatz wurde daher folgender Dart-Code entwickelt, der die CPU Architektur unter Linux per Aufruf von uname() per FFI ermittelt.

import 'dart:ffi'; // For FFI
import 'package:ffi/ffi.dart';

typedef NativeCall = int Function(Pointer);

/// Supported CPU architectures
enum CPU_ARCHITECTURE { x86, x86_64, arm, arm64, notSupported, undefinded }

final DynamicLibrary nativeAddLib = DynamicLibrary.open("libc.so.6");
NativeCall uname = nativeAddLib
    .lookup)>>("uname")
    .asFunction();

/// Class which holds the CPU architecture of the SoC.
/// Supported uname values provided by https://en.wikipedia.org/wiki/Uname 
class CpuArch {
  static CpuArch? _cpuArch;
  String machine;
  CPU_ARCHITECTURE cpuArch;

  factory CpuArch() {
    _cpuArch ??= CpuArch._internal();
    return _cpuArch as CpuArch;
  }

  CpuArch._internal()
      : machine = "",
        cpuArch = CPU_ARCHITECTURE.notSupported {
    Uname uname = nativeUname();
    machine = uname.machine;
    switch (uname.machine) {
      case 'i686':
      case 'i386':
        cpuArch = CPU_ARCHITECTURE.x86;
        break;
      case 'x86_64':
        cpuArch = CPU_ARCHITECTURE.x86_64;
        break;
      case 'aarch64':
      case 'aarch64_be':
      case 'arm64':
      case 'armv8b':
      case 'armv8l':
        cpuArch = CPU_ARCHITECTURE.arm64;
        break;
      case 'armv':
      case 'armv6l':
      case 'armv7l':
        cpuArch = CPU_ARCHITECTURE.arm;
        break;
    }
  }
}

/// Uname class, container for the Linux uname struct values.
class Uname {
  String sysname;
  String nodename;
  String release;
  String version;
  String machine;
  Uname(this.sysname, this.nodename, this.release, this.version, this.machine);
}

/// Calls the native uname() function.
Uname nativeUname() {
  // allocate a memory buffer for struct utsname - size value derived from this source
  // https://man7.org/linux/man-pages/man2/uname.2.html
  const len = 6 * 257; // maxium size
  const enumElements = 5;

  Pointer data = calloc(len);

  try {
    if (uname(data) != 0) {
      throw Exception('Calling uname() failed.');
    }

    // calculate _UTSNAME_LENGTH
    var utslen = 0;
    label:
    for (int i = 0; i < len; ++i) {
      if (data[i] == 0) {
        for (int j = i + 1; j < len; ++j) {
          if (data[j] != 0) {
            utslen = j;
            break label;
          }
        }
      }
    }

    var values = [];

    // extract these 5 strings from the memory
    //
    // char sysname[];    /* Operating system name (e.g., "Linux") */
    // char nodename[];   /* Name within "some implementation-defined network" */
    // char release[];    /* Operating system release (e.g., "2.6.28") */
    // char version[];    /* Operating system version */
    // char machine[];    /* Hardware identifier */
    for (int i = 0; i < enumElements; ++i) {
      var start = utslen * i;
      StringBuffer buf = StringBuffer();
      for (int i = start; i < len; ++i) {
        if (data[i] == 0) {
          break;
        }
        buf.write(String.fromCharCode(data[i]));
      }
      values.add(buf.toString());
    }
    return Uname(values[0], values[1], values[2], values[3], values[4]);
  } finally {
    malloc.free(data);
  }
}