launcher a42171a1→ddfe616e; 宿主 test_media ALL PASS + qemu 端到端(META注册→PLAYCTL下发闭环) 出入: 卡片以媒体会话为权威非通知; PLAYCTL 走会话fd(断连防御留后续)
155 lines
6.5 KiB
C
155 lines
6.5 KiB
C
/* test_ipc_harness.c — 宿主 IPC 服务端模拟(等价 launcher ipc_dispatch 的媒体子集).
|
||
*
|
||
* 用 meow_media.c + meow_ipc.h 复刻 launcher 的 IPC 服务器对媒体消息的处理,
|
||
* 供「qemu-mipsel meow_ipc listen/meta/playctl」端到端验证:
|
||
* listen → HELLO(MF_PLAYBACK)+META 注册源并等待下发;
|
||
* meta → 上报 META(作为新源/更新);
|
||
* playctl → 上传控制,服务端转发给「最近活跃会话」fd。
|
||
* 运行:gcc test_ipc_harness.c meow_media.c -o /tmp/meow_srv ; /tmp/meow_srv /tmp/e2e.sock
|
||
*/
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <stdint.h>
|
||
#include <unistd.h>
|
||
#include <fcntl.h>
|
||
#include <errno.h>
|
||
#include <stdlib.h>
|
||
#include <sys/socket.h>
|
||
#include <sys/un.h>
|
||
#include <sys/stat.h>
|
||
#include <poll.h>
|
||
#include "meow_ipc.h"
|
||
#include "meow_media.h"
|
||
|
||
#define MAXCLI 8
|
||
typedef struct {
|
||
int fd;
|
||
uint8_t hdr[MEOW_IPC_HDR_LEN]; int hdr_n;
|
||
uint8_t pay[MEOW_IPC_MAX]; uint32_t pay_tot, pay_n;
|
||
char app_id[MEOW_APP_ID_MAX];
|
||
} conn;
|
||
|
||
static conn g_c[MAXCLI];
|
||
|
||
static size_t srv_send(int fd, uint16_t type, uint32_t req, const void *payload, uint32_t len)
|
||
{
|
||
if (fd < 0) return 0;
|
||
uint8_t h[MEOW_IPC_HDR_LEN];
|
||
uint32_t magic = MEOW_IPC_MAGIC; uint16_t ver = MEOW_IPC_VERSION;
|
||
memcpy(h+0,&magic,4); memcpy(h+4,&ver,2); memcpy(h+6,&type,2); memcpy(h+8,&req,4); memcpy(h+12,&len,4);
|
||
size_t off=0;
|
||
while (off<sizeof h){ ssize_t w=write(fd,h+off,sizeof h-off); if(w<=0)return off; off+=(size_t)w; }
|
||
if(len&&payload){ size_t p=0; while(p<len){ ssize_t w=write(fd,(const uint8_t*)payload+p,len-p); if(w<=0)break; p+=(size_t)w; } off+=p; }
|
||
return off;
|
||
}
|
||
|
||
static conn *find_slot(int fd) { for (int i=0;i<MAXCLI;i++) if (g_c[i].fd==fd) return &g_c[i]; return NULL; }
|
||
static conn *alloc_slot(int fd){ for (int i=0;i<MAXCLI;i++) if (g_c[i].fd==-1){ g_c[i].fd=fd; g_c[i].hdr_n=0; g_c[i].pay_tot=g_c[i].pay_n=0; g_c[i].app_id[0]=0; return &g_c[i]; } return NULL; }
|
||
|
||
static void handle(conn *c)
|
||
{
|
||
uint16_t type; uint32_t req;
|
||
memcpy(&type, c->hdr+6, 2); memcpy(&req, c->hdr+8, 4);
|
||
switch (type) {
|
||
case MSG_HELLO: {
|
||
m_hello_t h; memset(&h,0,sizeof h); memcpy(&h,c->pay,c->pay_n<sizeof h?c->pay_n:sizeof h);
|
||
snprintf(c->app_id, sizeof c->app_id, "%s", h.app_id);
|
||
m_ack_t a; a.code=MEOW_OK; a.status=1; srv_send(c->fd,MSG_ACK,req,&a,sizeof a);
|
||
printf("SRV: HELLO app=%s flags=%u -> ACK\n", c->app_id, h.flags); break;
|
||
}
|
||
case MSG_META: {
|
||
m_meta_t m; memset(&m,0,sizeof m); memcpy(&m,c->pay,c->pay_n<sizeof m?c->pay_n:sizeof m);
|
||
m.app_id[MEOW_APP_ID_MAX-1]=0; m.title[127]=0; m.artist[127]=0; m.album[127]=0; m.artwork[191]=0; m.lyric[191]=0;
|
||
int rc = meow_media_meta(&m, c->fd);
|
||
m_ack_t a; a.code=MEOW_OK; a.status=(uint32_t)rc; srv_send(c->fd,MSG_ACK,req,&a,sizeof a);
|
||
const meow_media_session_t *cur = meow_media_current();
|
||
printf("SRV: META app=%s state=%u rc=%d | current=%s\n", m.app_id, m.state, rc,
|
||
cur?cur->app_id:"(none)");
|
||
break;
|
||
}
|
||
case MSG_PLAYCTL: {
|
||
m_playctl_t p; memset(&p,0,sizeof p); memcpy(&p,c->pay,c->pay_n<sizeof p?c->pay_n:sizeof p);
|
||
int rc = meow_media_playctl(p.cmd, p.seek_ms);
|
||
m_ack_t a; a.code=MEOW_OK; a.status=(uint32_t)rc; srv_send(c->fd,MSG_ACK,req,&a,sizeof a);
|
||
printf("SRV: PLAYCTL cmd=%u seek=%lld rc=%d (target current)\n", p.cmd, (long long)p.seek_ms, rc);
|
||
break;
|
||
}
|
||
default: {
|
||
m_err_t e; memset(&e,0,sizeof e); e.code=MEOW_ERR_INVAL; e.msg_len=5; memcpy(e.msg,"nope",5);
|
||
srv_send(c->fd,MSG_ERR,req,&e,sizeof e);
|
||
printf("SRV: unsupported type 0x%04x\n", type); break;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 读一个连接可用字节并组装帧(>=1 完整帧即 dispatch)。返回 0=ok,-1=连接关闭。 */
|
||
static int conn_read(conn *c)
|
||
{
|
||
for (;;) {
|
||
if ((unsigned)c->hdr_n < MEOW_IPC_HDR_LEN) {
|
||
ssize_t r = read(c->fd, c->hdr + c->hdr_n, MEOW_IPC_HDR_LEN - c->hdr_n);
|
||
if (r < 0) { if (errno==EAGAIN) return 0; return -1; }
|
||
if (r == 0) return -1;
|
||
c->hdr_n += (int)r;
|
||
if ((unsigned)c->hdr_n < MEOW_IPC_HDR_LEN) return 0;
|
||
uint32_t magic, len; uint16_t ver;
|
||
memcpy(&magic,c->hdr,4); memcpy(&ver,c->hdr+4,2); memcpy(&len,c->hdr+12,4);
|
||
if (magic!=MEOW_IPC_MAGIC||ver!=MEOW_IPC_VERSION||len>MEOW_IPC_MAX) return -1;
|
||
c->pay_tot=len; c->pay_n=0;
|
||
if (len==0){ handle(c); c->hdr_n=0; continue; }
|
||
}
|
||
if (c->pay_n < c->pay_tot) {
|
||
ssize_t r = read(c->fd, c->pay + c->pay_n, c->pay_tot - c->pay_n);
|
||
if (r < 0){ if (errno==EAGAIN) return 0; return -1; }
|
||
if (r==0) return -1;
|
||
c->pay_n += (uint32_t)r;
|
||
if (c->pay_n < c->pay_tot) return 0;
|
||
}
|
||
handle(c);
|
||
c->hdr_n=0; c->pay_n=0;
|
||
}
|
||
}
|
||
|
||
int main(int argc, char **argv)
|
||
{
|
||
const char *path = (argc>1)?argv[1]:"/tmp/meow_ipc_e2e.sock";
|
||
for (int i=0;i<MAXCLI;i++) g_c[i].fd=-1;
|
||
meow_media_ops ops;
|
||
ops.send=srv_send; ops.log=NULL; meow_media_init(&ops);
|
||
|
||
int lfd=socket(AF_UNIX,SOCK_STREAM,0);
|
||
unlink(path);
|
||
struct sockaddr_un a; memset(&a,0,sizeof a); a.sun_family=AF_UNIX; strncpy(a.sun_path,path,sizeof a.sun_path-1);
|
||
if (bind(lfd,(struct sockaddr*)&a,sizeof a)<0){ perror("bind"); return 1; }
|
||
chmod(path,0666);
|
||
if (listen(lfd,8)<0){ perror("listen"); return 1; }
|
||
fcntl(lfd,F_SETFL,O_NONBLOCK);
|
||
printf("SRV: listening %s\n", path); fflush(stdout);
|
||
|
||
struct pollfd fds[MAXCLI+1]; int nfds=1;
|
||
fds[0].fd=lfd; fds[0].events=POLLIN; fds[0].revents=0;
|
||
while (1) {
|
||
fds[0].fd=lfd; fds[0].events=POLLIN;
|
||
int n=1;
|
||
for (int i=0;i<MAXCLI;i++) if (g_c[i].fd>=0){ fds[n].fd=g_c[i].fd; fds[n].events=POLLIN; fds[n].revents=0; n++; }
|
||
nfds=n;
|
||
int r=poll(fds,nfds,400);
|
||
if (r<0){ if(errno==EINTR)continue; break; }
|
||
if (fds[0].revents&POLLIN){
|
||
for(;;){ int c=accept(lfd,NULL,NULL); if(c<0)break; fcntl(c,F_SETFL,O_NONBLOCK);
|
||
if(!alloc_slot(c)){ close(c); printf("SRV: reject conn (full)\n"); } else printf("SRV: accept fd=%d\n", c); }
|
||
}
|
||
for (int i=1;i<nfds;i++){
|
||
int ev=fds[i].revents;
|
||
if (ev&(POLLIN|POLLHUP|POLLERR|POLLNVAL)){
|
||
conn *c=find_slot(fds[i].fd);
|
||
if (!c) continue;
|
||
if (conn_read(c)<0){ printf("SRV: conn %d closed\n", c->fd); close(c->fd); c->fd=-1; }
|
||
}
|
||
}
|
||
fflush(stdout);
|
||
}
|
||
close(lfd); unlink(path);
|
||
return 0;
|
||
}
|