diff --git a/.gitignore b/.gitignore index aa5d4b2..a029b2d 100644 --- a/.gitignore +++ b/.gitignore @@ -174,3 +174,4 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ +dummy_sender/dummy_sender diff --git a/README.md b/README.md index 7e6c02d..3f721a7 100644 --- a/README.md +++ b/README.md @@ -37,5 +37,20 @@ systemctl enable --now /lib/systemd/system/reHDDPrinter.service see https://github.com/pklaus/brother_ql for details for printer access +## Test IPC msg queue +### Dummy Sender +``` +cd dummy_sender +clear && g++ -Wall main.cpp -o dummy_sender +clear && ./dummy_sender +``` +### Dummy Receiver +``` +clear && python ./dummy_receiver.py +``` +### Clear IPC mgs queue +``` +clear && bash ./cleanup_queues.py +``` \ No newline at end of file diff --git a/cleanup_queue.sh b/cleanup_queue.sh new file mode 100644 index 0000000..38238a8 --- /dev/null +++ b/cleanup_queue.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# Cleanup script to remove the IPC message queue + +MSG_QUEUE_KEY="0x1b11193c0" + +echo "Removing message queue with key: $MSG_QUEUE_KEY" +ipcrm -Q $MSG_QUEUE_KEY 2>/dev/null + +if [ $? -eq 0 ]; then + echo "Message queue removed successfully" +else + echo "No message queue found or already removed" +fi + +echo "Current message queues:" +ipcs -q diff --git a/dummy_receiver.py b/dummy_receiver.py index bfcd21d..0ca3bdc 100644 --- a/dummy_receiver.py +++ b/dummy_receiver.py @@ -1,53 +1,185 @@ -import sysv_ipc #pip install sysv-ipc -import pycstruct #pip install pycstruct +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +"""Author: Hendrik Schutter, localhorst@mosad.xyz +Date of creation: 2025/12/05 +Date of last modification: 2025/12/05 +""" + +import ctypes +import os +import time +import argparse + +# Constants +STR_BUFFER_SIZE = 64 +MSG_QUEUE_KEY = 0x1B11193C0 +IPC_CREAT = 0o1000 + +terminate = False -str_buffer_size = 64 -msg_queue_key = 0x1B11193C0 +class TDriveData(ctypes.Structure): + _fields_ = [ + ("caDriveIndex", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveHours", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveCycles", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveErrors", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveShredTimestamp", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveShredDuration", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveCapacity", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveState", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveModelFamily", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveModelName", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveSerialnumber", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveReHddVersion", ctypes.c_char * STR_BUFFER_SIZE), + ] -try: - mq = sysv_ipc.MessageQueue(msg_queue_key, sysv_ipc.IPC_CREAT) +class TMsgQueueData(ctypes.Structure): + _fields_ = [ + ("msg_queue_type", ctypes.c_long), + ("driveData", TDriveData), + ] + + +# IPC bindings - enable errno support +libc = ctypes.CDLL("libc.so.6", use_errno=True) +msgget = libc.msgget +msgrcv = libc.msgrcv + +msgget.argtypes = [ctypes.c_int, ctypes.c_int] +msgget.restype = ctypes.c_int + +msgrcv.argtypes = [ + ctypes.c_int, + ctypes.POINTER(TMsgQueueData), + ctypes.c_size_t, + ctypes.c_long, + ctypes.c_int, +] +msgrcv.restype = ctypes.c_ssize_t + + +def create_drive_objects(drive_info): + """Convert dictionary to layouter-compatible DriveData and ReHddInfo objects""" + drive = layouter.DriveData( + drive_index=int(drive_info["driveIndex"]), + drive_state=drive_info["driveState"], + modelfamily=drive_info["driveModelFamily"], + modelname=drive_info["driveModelName"], + capacity=int(drive_info["driveCapacity"]), + serialnumber=drive_info["driveSerialnumber"], + power_on_hours=int(drive_info["driveHours"]), + power_cycle=int(drive_info["driveCycles"]), + smart_error_count=int(drive_info["driveErrors"]), + shred_timestamp=int(drive_info["driveShredTimestamp"]), + shred_duration=int(drive_info["driveShredDuration"]), + ) + + rehdd_info = layouter.ReHddInfo( + link="https://git.mosad.xyz/localhorst/reHDD", + version=drive_info["driveReHddVersion"], + ) + + return drive, rehdd_info + + +def worker(queue_id, test_mode=False): + try: + while not terminate: + if test_mode: + time.sleep(3) + drive_info = { + "driveIndex": "42", + "driveHours": 44, + "driveCycles": 45, + "driveErrors": 43, + "driveShredTimestamp": int(time.time()), + "driveShredDuration": 0, + "driveCapacity": 42, + "driveState": "shredded", + "driveModelFamily": "modelFamily", + "driveModelName": "modelName", + "driveSerialnumber": "serial", + "driveReHddVersion": "V1.1.2", + } + else: + msg = TMsgQueueData() + print("Waiting for message from queue...") + + # Calculate message size - must match C++ side: sizeof(t_msgQueueData) - sizeof(long) + # This is the size of the data portion (excluding msg_queue_type) + msg_size = ctypes.sizeof(TMsgQueueData) - ctypes.sizeof(ctypes.c_long) + + print(f"Message size to receive: {msg_size} bytes") + + result = msgrcv( + queue_id, + ctypes.byref(msg), + msg_size, + 0, # msg type (0 = get first message) + 0, # flags (0 = blocking) + ) + + if result == -1: + err = ctypes.get_errno() + print( + f"Error reading from message queue: {os.strerror(err)} (errno: {err})" + ) + break + + print(f"Received {result} bytes from queue") + + d = msg.driveData + drive_info = { + "driveIndex": d.caDriveIndex.decode().strip("\x00"), + "driveHours": int(d.caDriveHours.decode().strip("\x00")), + "driveCycles": int(d.caDriveCycles.decode().strip("\x00")), + "driveErrors": int(d.caDriveErrors.decode().strip("\x00")), + "driveShredTimestamp": int( + d.caDriveShredTimestamp.decode().strip("\x00") + ), + "driveShredDuration": int( + d.caDriveShredDuration.decode().strip("\x00") + ), + "driveCapacity": int(d.caDriveCapacity.decode().strip("\x00")), + "driveState": d.caDriveState.decode().strip("\x00"), + "driveModelFamily": d.caDriveModelFamily.decode().strip("\x00"), + "driveModelName": d.caDriveModelName.decode().strip("\x00"), + "driveSerialnumber": d.caDriveSerialnumber.decode().strip("\x00"), + "driveReHddVersion": d.caDriveReHddVersion.decode().strip("\x00"), + } + + print(f"Received Drive Data: {drive_info}") + except Exception as e: + import traceback + + print(f"Worker encountered an error: {e}") + traceback.print_exc() + + +def main(): while True: - message, mtype = mq.receive() - print("") - #print("*** New message received ***") - # print(f"Raw message: {message}") + try: + # Create or connect to the message queue with IPC_CREAT flag + # This matches the C++ sender's flags (IPC_CREAT | 0666) + queue_id = msgget(MSG_QUEUE_KEY, IPC_CREAT | 0o666) + if queue_id == -1: + err = ctypes.get_errno() + raise RuntimeError( + f"Failed to create/connect to the message queue: {os.strerror(err)}" + ) -#uint8_t u8DriveIndex; -#uint32_t u32DriveHours; -#uint32_t u32DriveCycles; -#uint32_t u32DriveError; -#uint64_t u64DriveShredTimestamp; -#uint64_t u64DriveShredDuration; -#uint64_t u64DriveCapacity; -#char caDriveState[STR_BUFFER_SIZE]; -#char caDriveModelFamiliy[STR_BUFFER_SIZE]; -#char caDriveModelName[STR_BUFFER_SIZE]; -#char caDriveSerialnumber[STR_BUFFER_SIZE]; - - - driveData = pycstruct.StructDef() - driveData.add('utf-8', 'driveIndex', length=str_buffer_size) - driveData.add('utf-8', 'driveHours', length=str_buffer_size) - driveData.add('utf-8', 'driveCycles', length=str_buffer_size) - driveData.add('utf-8', 'driveErrors', length=str_buffer_size) - driveData.add('utf-8', 'driveShredTimestamp', length=str_buffer_size) - driveData.add('utf-8', 'driveShredDuration', length=str_buffer_size) - driveData.add('utf-8', 'driveCapacity', length=str_buffer_size) - driveData.add('utf-8', 'driveState', length=str_buffer_size) - driveData.add('utf-8', 'driveModelFamiliy', length=str_buffer_size) - driveData.add('utf-8', 'driveModelModel', length=str_buffer_size) - driveData.add('utf-8', 'driveSerialnumber', length=str_buffer_size) - driveData.add('utf-8', 'driveReHddVersion', length=str_buffer_size) - - # Dictionary representation - result = driveData.deserialize(message) - print('Dictionary object:') - print(str(result)) - - -except sysv_ipc.ExistentialError: - print("ERROR: message queue creation failed") + print(f"Successfully connected to message queue (ID: {queue_id})") + worker(queue_id) + except Exception as e: + import traceback + + print(f"Main process encountered an error: {e}") + traceback.print_exc() + time.sleep(30) +if __name__ == "__main__": + main() diff --git a/dummy_sender/main.cpp b/dummy_sender/main.cpp new file mode 100644 index 0000000..ea63cb1 --- /dev/null +++ b/dummy_sender/main.cpp @@ -0,0 +1,77 @@ +/** + * @file main.cpp + * @brief Send drive data to printer service using ipc msg queue + * @author Hendrik Schutter + * @date 06.12.2025 + */ + +#include "main.h" + +#define REHDD_VERSION "V99.99.99" + +/** + * \brief app entry point + * \param void + * \return Status-Code + */ +int main(void) +{ + int msqid; + std::cout << "Dummy sender for IPC queue" << std::endl; + + if (-1 == (msqid = msgget((key_t)IPC_MSG_QUEUE_KEY, IPC_CREAT | 0666))) + { + std::cout << "Printer: Create msg queue failed! Error: " << strerror(errno) << std::endl; + return EXIT_FAILURE; + } + else + { + std::cout << "Printer: Created/connected to msg queue (ID: " << msqid << ")" << std::endl; + } + + t_msgQueueData msgQueueData; + msgQueueData.msg_queue_type = 1; + + sprintf(msgQueueData.driveData.caDriveIndex, "%i", 0); + sprintf(msgQueueData.driveData.caDriveState, "shredded"); + strcpy(msgQueueData.driveData.caDriveModelFamily, "Toshiba 2.5 HDD MK..65GSSX"); + strcpy(msgQueueData.driveData.caDriveModelName, "TOSHIBA MK3265GSDX"); + sprintf(msgQueueData.driveData.caDriveCapacity, "%li", 343597383000LU); + strcpy(msgQueueData.driveData.caDriveSerialnumber, "YG6742U56UDRL123456789ABCDEFGJKL"); + sprintf(msgQueueData.driveData.caDriveHours, "%i", 7074); + sprintf(msgQueueData.driveData.caDriveCycles, "%i", 4792); + sprintf(msgQueueData.driveData.caDriveErrors, "%i", 1); + sprintf(msgQueueData.driveData.caDriveShredTimestamp, "%li", 71718LU); + sprintf(msgQueueData.driveData.caDriveShredDuration, "%li", 81718LU); + /* + switch (drive->connectionType) + { + case Drive::USB: + strcpy(msgQueueData.driveData.caDriveConnectionType, "usb"); + break; + case Drive::SATA: + strcpy(msgQueueData.driveData.caDriveConnectionType, "sata"); + break; + case Drive::NVME: + strcpy(msgQueueData.driveData.caDriveConnectionType, "nvme"); + break; + case Drive::UNKNOWN: + default: + strcpy(msgQueueData.driveData.caDriveConnectionType, "na"); + } + */ + sprintf(msgQueueData.driveData.caDriveReHddVersion, REHDD_VERSION); + + std::cout << "Sending message to queue..." << std::endl; + if (-1 == msgsnd(msqid, &msgQueueData, sizeof(t_msgQueueData) - sizeof(long), 0)) + { + std::cout << "Printer: Send msg queue failed! Error: " << strerror(errno) << std::endl; + return EXIT_FAILURE; + } + else + { + std::cout << "Printer: print triggered successfully" << std::endl; + } + + return EXIT_SUCCESS; +} diff --git a/dummy_sender/main.h b/dummy_sender/main.h new file mode 100644 index 0000000..2ed16cd --- /dev/null +++ b/dummy_sender/main.h @@ -0,0 +1,45 @@ +/** + * @file main.h + * @brief Send drive data to printer service using ipc msg queue + * @author Hendrik Schutter + * @date 06.12.2025 + */ + +#ifndef PRINTER_H_ +#define PRINTER_H_ + +#include +#include +#include +#include +#include +#include + +#define STR_BUFFER_SIZE 64U +#define IPC_MSG_QUEUE_KEY 0x1B11193C0 + +typedef struct +{ + char caDriveIndex[STR_BUFFER_SIZE]; + char caDriveHours[STR_BUFFER_SIZE]; + char caDriveCycles[STR_BUFFER_SIZE]; + char caDriveErrors[STR_BUFFER_SIZE]; + char caDriveShredTimestamp[STR_BUFFER_SIZE]; + char caDriveShredDuration[STR_BUFFER_SIZE]; + char caDriveCapacity[STR_BUFFER_SIZE]; + char caDriveState[STR_BUFFER_SIZE]; + // char caDriveConnectionType[STR_BUFFER_SIZE]; + char caDriveModelFamily[STR_BUFFER_SIZE]; + char caDriveModelName[STR_BUFFER_SIZE]; + char caDriveSerialnumber[STR_BUFFER_SIZE]; + char caDriveReHddVersion[STR_BUFFER_SIZE]; + +} t_driveData; + +typedef struct +{ + long msg_queue_type; + t_driveData driveData; +} t_msgQueueData; + +#endif // PRINTER_H_ diff --git a/reHDDPrinter.py b/reHDDPrinter.py index d9f18c9..0d6fa13 100644 --- a/reHDDPrinter.py +++ b/reHDDPrinter.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -""" Author: Hendrik Schutter, localhorst@mosad.xyz - Date of creation: 2022/11/23 - Date of last modification: 2025/06/15 +"""Author: Hendrik Schutter, localhorst@mosad.xyz +Date of creation: 2022/11/23 +Date of last modification: 2025/06/15 """ import ctypes @@ -119,6 +119,7 @@ def create_drive_objects(drive_info): def worker(queue_id, test_mode=False): try: while not terminate: + time.sleep(3) if test_mode: drive_info = { "driveIndex": "42", @@ -167,7 +168,6 @@ def worker(queue_id, test_mode=False): "driveSerialnumber": d.caDriveSerialnumber.decode().strip("\x00"), "driveReHddVersion": d.caDriveReHddVersion.decode().strip("\x00"), } - time.sleep(3) print(f"Received Drive Data: {drive_info}") @@ -204,9 +204,16 @@ def main(): return while True: try: - queue_id = msgget(MSG_QUEUE_KEY, 0) + # Create or connect to the message queue with IPC_CREAT flag + # This matches the C++ sender's flags (IPC_CREAT | 0666) + queue_id = msgget(MSG_QUEUE_KEY, IPC_CREAT | 0o666) if queue_id == -1: - raise RuntimeError("Failed to connect to the existing message queue.") + err = ctypes.get_errno() + raise RuntimeError( + f"Failed to create/connect to the message queue: {os.strerror(err)}" + ) + + print(f"Successfully connected to message queue (ID: {queue_id})") worker(queue_id) except Exception as e: print(f"Main process encountered an error: {e}")