diff --git a/data/build_snap.sh b/data/build_snap.sh index 825ce38627..a7a8d2b592 100755 --- a/data/build_snap.sh +++ b/data/build_snap.sh @@ -9,6 +9,6 @@ cd .. sed -i "s/version\: git/version\: $(python3 src/urh/version.py)/" snapcraft.yaml -SNAPCRAFT_BUILD_ENVIRONMENT_CPU=4 SNAPCRAFT_BUILD_ENVIRONMENT_MEMORY=6G snapcraft +SNAPCRAFT_BUILD_ENVIRONMENT_CPU=6 SNAPCRAFT_BUILD_ENVIRONMENT_MEMORY=18G snapcraft sudo snap install --dangerous urh*.snap urh diff --git a/data/semwraplib.c b/data/semwraplib.c new file mode 100644 index 0000000000..73f28e35f4 --- /dev/null +++ b/data/semwraplib.c @@ -0,0 +1,280 @@ +// Source: https://github.com/snapcore/snapcraft-preloads/blob/master/semaphores/preload-semaphores.c + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +/* + * $ gcc -Wall -fPIC -shared -o mylib.so ./lib.c -ldl + * $ LD_PRELOAD=./mylib.so ... + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static sem_t *(*original_sem_open) (const char *, int, ...); +static int (*original_sem_unlink) (const char *); + +// Format is: 'sem.snap.SNAP_NAME.'. So: 'sem.snap.' + '.' = 10 +#define MAX_NAME_SIZE NAME_MAX - 10 +#define SHM_DIR "/dev/shm" + +void debug(char *s, ...) +{ + if (secure_getenv("SEMWRAP_DEBUG")) { + va_list va; + va_start(va, s); + fprintf(stderr, "SEMWRAP: "); + vfprintf(stderr, s, va); + va_end(va); + fprintf(stderr, "\n"); + } +} + +const char *get_snap_name(void) +{ + const char *snapname = getenv("SNAP_INSTANCE_NAME"); + if (!snapname) { + snapname = getenv("SNAP_NAME"); + } + if (!snapname) { + debug("SNAP_NAME and SNAP_INSTANCE_NAME not set"); + } + return snapname; +} + +int rewrite(const char *snapname, const char *name, char *rewritten, + size_t rmax) +{ + if (strlen(snapname) + strlen(name) > MAX_NAME_SIZE) { + errno = ENAMETOOLONG; + return -1; + } + + const char *tmp = name; + if (tmp[0] == '/') { + // If specified with leading '/', just strip it to avoid + // having to mkdir(), etc + tmp = &name[1]; + } + + int n = snprintf(rewritten, rmax, "snap.%s.%s", snapname, tmp); + if (n < 0 || n >= rmax) { + fprintf(stderr, "snprintf truncated\n"); + return -1; + } + rewritten[rmax-1] = '\0'; + + return 0; +} + +sem_t *sem_open(const char *name, int oflag, ...) +{ + mode_t mode; + unsigned int value; + + debug("sem_open()"); + debug("requested name: %s", name); + + // lookup the libc's sem_open() if we haven't already + if (!original_sem_open) { + dlerror(); + original_sem_open = dlsym(RTLD_NEXT, "sem_open"); + if (!original_sem_open) { + debug("could not find sem_open in libc"); + return SEM_FAILED; + } + dlerror(); + } + + // mode and value must be set with O_CREAT + va_list argp; + va_start(argp, oflag); + if (oflag & O_CREAT) { + mode = va_arg(argp, mode_t); + value = va_arg(argp, unsigned int); + if (value > SEM_VALUE_MAX) { + errno = EINVAL; + return SEM_FAILED; + } + } + va_end(argp); + + const char *snapname = get_snap_name(); + + // just call libc's sem_open() if snapname not set + if (!snapname) { + if (oflag & O_CREAT) { + return original_sem_open(name, oflag, mode, value); + } + return original_sem_open(name, oflag); + } + + // Format the rewritten name + char rewritten[MAX_NAME_SIZE+1]; + if (rewrite(snapname, name, rewritten, MAX_NAME_SIZE + 1) != 0) { + return SEM_FAILED; + } + debug("rewritten name: %s", rewritten); + + if (oflag & O_CREAT) { + // glibc's sem_open with O_CREAT will create a file in /dev/shm + // by creating a tempfile, initializing it, hardlinking it and + // unlinking the tempfile. We: + // 1. create a temporary file in /dev/shm with rewritten path + // as the template and the specified mode + // 2. initializing a sem_t with sem_init + // 3. writing the initialized sem_t to the temporary file using + // sem_open()s declared value. We used '1' for pshared since + // that is how glibc sets up a named semaphore + // 4. close the temporary file + // 5. hard link the temporary file to the rewritten path. If + // O_EXCL is not specified, ignore EEXIST and just cleanup + // as per documented behavior in 'man sem_open'. If O_EXCL + // is specified and file exists, exit with error. If link is + // successful, cleanup. + // 6. call glibc's sem_open() without O_CREAT|O_EXCL + // + // See glibc's fbtl/sem_open.c for more details + + // First, calculate the requested path + char path[PATH_MAX] = { 0 }; + // /sem. + '/0' = 14 + int max_path_size = strlen(SHM_DIR) + strlen(rewritten) + 6; + if (max_path_size >= PATH_MAX) { + // Should never happen since PATH_MAX should be much + // larger than NAME_MAX, but be defensive. + errno = ENAMETOOLONG; + return SEM_FAILED; + } + int n = snprintf(path, max_path_size, "%s/sem.%s", SHM_DIR, + rewritten); + if (n < 0 || n >= max_path_size) { + errno = ENAMETOOLONG; + return SEM_FAILED; + } + path[max_path_size-1] = '\0'; + + // Then calculate the template path + char tmp[PATH_MAX] = { 0 }; + n = snprintf(tmp, PATH_MAX, "%s/%s.XXXXXX", SHM_DIR, + rewritten); + if (n < 0 || n >= PATH_MAX) { + errno = ENAMETOOLONG; + return SEM_FAILED; + } + tmp[PATH_MAX-1] = '\0'; + + // Next, create a temporary file + int fd = mkstemp(tmp); + if (fd < 0) { + return SEM_FAILED; + } + debug("tmp name: %s", tmp); + + // Update the temporary file to have the requested mode + if (fchmod(fd, mode) < 0) { + close(fd); + unlink(tmp); + return SEM_FAILED; + } + + // Then write out an empty semaphore and set the initial value. + // We use '1' for pshared since that is how glibc sets up the + // semaphore (see glibc's fbtl/sem_open.c) + sem_t initsem; + sem_init(&initsem, 1, value); + if (write(fd, &initsem, sizeof(sem_t)) < 0) { + close(fd); + unlink(tmp); + return SEM_FAILED; + } + close(fd); + + // Then link the file into place. If the target exists and + // O_EXCL was not specified, just cleanup and proceed to open + // the existing file as per documented behavior in 'man + // sem_open'. + int existed = 0; + if (link(tmp, path) < 0) { + // Note: snapd initially didn't allow 'l' in its + // policy so we first try with link() since it is + // race-free but fallback to rename() if necessary. + if (errno == EACCES || errno == EPERM) { + fprintf(stderr, "sem_open() wrapper: hard linking tempfile denied. Falling back to rename()\n"); + if (rename(tmp, path) < 0) { + unlink(tmp); + return SEM_FAILED; + } + } else if (oflag & O_EXCL || errno != EEXIST) { + unlink(tmp); + return SEM_FAILED; + } + existed = 1; + } + unlink(tmp); + + // Then call sem_open() on the created file, stripping out the + // O_CREAT|O_EXCL since we just created it + sem_t *sem = original_sem_open(rewritten, + oflag & ~(O_CREAT | O_EXCL)); + if (sem == SEM_FAILED) { + if (!existed) { + unlink(path); + } + return SEM_FAILED; + } + + return sem; + } else { + // without O_CREAT, just call sem_open with rewritten + return original_sem_open(rewritten, oflag); + } + + return SEM_FAILED; +} + +// sem_unlink +int sem_unlink(const char *name) +{ + debug("sem_unlink()"); + debug("requested name: %s", name); + + // lookup the libc's sem_unlink() if we haven't already + if (!original_sem_unlink) { + dlerror(); + original_sem_unlink = dlsym(RTLD_NEXT, "sem_unlink"); + if (!original_sem_unlink) { + debug("could not find sem_unlink in libc"); + return -1; + } + dlerror(); + } + + const char *snapname = get_snap_name(); + + // just call libc's sem_unlink() if snapname not set + if (!snapname) { + return original_sem_unlink(name); + } + + // Format the rewritten name + char rewritten[MAX_NAME_SIZE+1]; + if (rewrite(snapname, name, rewritten, MAX_NAME_SIZE + 1) != 0) { + return -1; + } + debug("rewritten name: %s", rewritten); + + return original_sem_unlink(rewritten); +} \ No newline at end of file diff --git a/data/snapcraft.yaml b/data/snapcraft.yaml index 1bb5a65ed8..c702c85844 100644 --- a/data/snapcraft.yaml +++ b/data/snapcraft.yaml @@ -44,7 +44,7 @@ plugs: # plugs for theming, font settings, cursor and to use gtk3 file chooser apps: urh: adapter: full - command: snap/command-chain/alsa-launch desktop-launch snapcraft-preload $SNAP/bin/urh + command: snap/command-chain/alsa-launch desktop-launch $SNAP/bin/urh plugs: - desktop - desktop-legacy @@ -60,21 +60,14 @@ apps: - network environment: QT_QPA_PLATFORMTHEME: gtk3 + LD_PRELOAD: $SNAP/lib/semwraplib.so + BLADERF_SEARCH_DIR: $SNAP/bladefpgas layout: /usr/lib/$SNAPCRAFT_ARCH_TRIPLET/alsa-lib: bind: $SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/alsa-lib parts: - snapcraft-preload: - source: https://github.com/sergiusens/snapcraft-preload.git - plugin: cmake - build-packages: - - on amd64: - - gcc-multilib - - g++-multilib - stage-packages: [lib32stdc++6] - alsa-mixin: plugin: dump source: https://github.com/diddlesnaps/snapcraft-alsa.git @@ -85,6 +78,16 @@ parts: - libasound2 - libasound2-plugins + semwraplib: + plugin: nil + source: data + override-build: | + snapcraftctl build + mkdir -p $SNAPCRAFT_PART_INSTALL/lib + gcc -g -O0 -Wall -Wstrict-prototypes -fPIC -shared semwraplib.c -o $SNAPCRAFT_PART_INSTALL/lib/semwraplib.so -ldl + build-packages: + - build-essential + desktop-qt5: source: https://github.com/ubuntu/snapcraft-desktop-helpers.git source-subdir: qt @@ -117,36 +120,44 @@ parts: - libxcursor1 - libxinerama1 - libxrandr2 - - libasyncns0 - - libatk-bridge2.0-0 - - libatspi2.0-0 - - libcairo-gobject2 - - libepoxy0 - - libflac8 - - libgtk-3-0 - - libogg0 - - libpulse0 - - libsndfile1 - - libvorbis0a - - libvorbisenc2 - - libwayland-cursor0 + - libasyncns0 + - libatk-bridge2.0-0 + - libatspi2.0-0 + - libcairo-gobject2 + - libepoxy0 + - libflac8 + - libgtk-3-0 + - libogg0 + - libpulse0 + - libsndfile1 + - libvorbis0a + - libvorbisenc2 + - libwayland-cursor0 - libwayland-egl1 - libodbc1 - libpq5 - libpulse-mainloop-glib0 - libspeechd2 - + urh: after: [desktop-qt5, sdrplay, alsa-mixin] plugin: python - python-version: python3 requirements: [data/requirements.txt] source: . + override-build: | + git clone https://github.com/Nuand/bladeRF.git + cd bladeRF/host + mkdir build + cmake -DCMAKE_INSTALL_PREFIX=$SNAPCRAFT_PART_INSTALL .. + make -j$(nproc) + make install + cmake -DCMAKE_INSTALL_PREFIX=/usr .. + make install + snapcraftctl build build-packages: - python3 - python3-pyqt5 - libairspy-dev - - libbladerf-dev - libhackrf-dev - libiio-dev - liblimesuite-dev @@ -154,12 +165,14 @@ parts: - libuhd-dev - wget - execstack + - build-essential + - cmake + - pkg-config stage-packages: - python3 - python3-pyqt5 - python3-pyaudio - libairspy-dev - - libbladerf-dev - libhackrf-dev - libiio-dev - liblimesuite-dev @@ -185,3 +198,13 @@ parts: cp x86_64/* . ln -sf /lib/libmirsdrapi-rsp.so.2.13 /lib/libmirsdrapi-rsp.so snapcraftctl build + + bladerf-fpga: + plugin: dump + source: . + override-build: | + snapcraftctl build + wget https://www.nuand.com/fpga/v0.11.0/hostedxA4.rbf -P $SNAPCRAFT_PART_INSTALL/bladefpgas + wget https://www.nuand.com/fpga/v0.11.0/hostedxA9.rbf -P $SNAPCRAFT_PART_INSTALL/bladefpgas + wget https://www.nuand.com/fpga/v0.11.0/hostedx40.rbf -P $SNAPCRAFT_PART_INSTALL/bladefpgas + wget https://www.nuand.com/fpga/v0.11.0/hostedx115.rbf -P $SNAPCRAFT_PART_INSTALL/bladefpgas