recv & refine & add modem manager py

This commit is contained in:
JerryXiao 2022-11-11 18:18:47 +08:00
parent 6cf52b0e41
commit 4c2e8f14e0
Signed by: Jerry
GPG key ID: 22618F758B5BE2E5
8 changed files with 713 additions and 132 deletions

View file

@ -1,26 +1,33 @@
PJSIP_DIR=pjproject-2.11.1 PJSIP_DIR=pjproject
PKG_CONFIG_PATH=pjsip.install/lib/pkgconfig PKG_CONFIG_PATH=pjsip.install/lib/pkgconfig
all: d-modem slmodemd ifndef NO_PULSE
DM_LDFLAGS += -l pulse -l pulse-simple
DM_CFLAGS += -DHAS_PULSE
endif
all: d-modem slmodemd $(if $(NO_PULSE),,d-modem.nopulse)
$(PKG_CONFIG_PATH)/libpjproject.pc: $(PKG_CONFIG_PATH)/libpjproject.pc:
(cd $(PJSIP_DIR); [ -f ./config.status ] || ./configure --prefix=`pwd`/../pjsip.install --disable-video) (cd $(PJSIP_DIR); [ -f ./config.status ] || ./configure --prefix=`pwd`/../pjsip.install --disable-video --disable-sound --enable-ext-sound --disable-speex-aec --enable-g711-codec --disable-l16-codec --disable-gsm-codec --disable-g722-codec --disable-g7221-codec --disable-speex-codec --disable-ilbc-codec --disable-sdl --disable-ffmpeg --disable-v4l2 --disable-openh264 --disable-vpx --disable-android-mediacodec --disable-darwin-ssl --disable-ssl --disable-opencore-amr --disable-silk --disable-opus --disable-bcg729 --disable-libyuv --disable-libwebrtc)
$(MAKE) -C $(PJSIP_DIR) && \ $(MAKE) -C $(PJSIP_DIR) && \
$(MAKE) -C $(PJSIP_DIR) install $(MAKE) -C $(PJSIP_DIR) install
d-modem: d-modem.c $(PKG_CONFIG_PATH)/libpjproject.pc d-modem d-modem.nopulse : d-modem.c $(PKG_CONFIG_PATH)/libpjproject.pc
$(CC) -o $@ $< `PKG_CONFIG_PATH="$(PKG_CONFIG_PATH)" pkg-config --static --cflags --libs libpjproject` $(CC) $(if $(findstring nopulse,$@),,$(DM_CFLAGS) $(DM_LDFLAGS)) -o $@ $< \
`PKG_CONFIG_PATH="$(PKG_CONFIG_PATH)" pkg-config --static --cflags --libs libpjproject` \
-O2 -s
slmodemd: slmodemd:
$(MAKE) -C slmodemd $(MAKE) -C slmodemd
clean: clean:
rm -f d-modem.o d-modem rm -f d-modem.o d-modem d-modem.nopulse
$(MAKE) -C slmodemd clean $(MAKE) -C slmodemd clean
realclean: clean realclean: clean
$(MAKE) -C $(PJSIP_DIR) realclean $(MAKE) -C $(PJSIP_DIR) realclean
rm -rf pjsip.install/* rm -rf pjsip.install/*
.PHONY: all clean realclean slmodemd .PHONY: all clean realclean slmodemd

243
d-modem.c
View file

@ -16,6 +16,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <stdbool.h> #include <stdbool.h>
#include <time.h> #include <time.h>
@ -24,14 +25,38 @@
#include <errno.h> #include <errno.h>
// test // test
#include <pjsua-lib/pjsua.h> #include <pjsua-lib/pjsua.h>
#include <pjsua-lib/pjsua_internal.h>
#ifdef HAS_PULSE
#include <pulse/simple.h>
#endif
#define PCM_FILE "dmodem.%s.s16le.9600hz.pcm"
#undef PCM_FILE
//#define HAS_PULSE
#ifdef HAS_PULSE
pa_simple *pa_s1 = NULL;
pa_simple *pa_s2 = NULL;
const pa_sample_spec pa_ss = {
.format = PA_SAMPLE_S16LE,
.rate = 9600,
.channels = 1
};
#endif
#ifdef PCM_FILE
int wave_recv = -1;
int wave_transmit = -1;
#endif
#define SIGNATURE PJMEDIA_SIG_CLASS_PORT_AUD('D','M') #define SIGNATURE PJMEDIA_SIG_CLASS_PORT_AUD('D','M')
#define DMODEM_DIAL_MODE 0 #define DMODEM_DIAL_MODE 0
#define DMODEM_ANSWER_MODE 2 #define DMODEM_ANSWER_MODE 1
#define DMODEM_RING_DETECT_MODE 3
uint8_t mode = DMODEM_DIAL_MODE; uint8_t mode = DMODEM_DIAL_MODE;
uint8_t ringing = 0; uint8_t ringing = 0;
uint8_t answered = 0;
int ppid;
pjsua_call_id incoming; pjsua_call_id incoming;
struct dmodem { struct dmodem {
@ -44,20 +69,85 @@ static struct dmodem port;
static bool destroying = false; static bool destroying = false;
static pj_pool_t *pool; static pj_pool_t *pool;
void stop_pa();
static void error_exit(const char *title, pj_status_t status) { static void error_exit(const char *title, pj_status_t status) {
pjsua_perror(__FILE__, title, status); pjsua_perror(__FILE__, title, status);
if (!destroying) { if (!destroying) {
destroying = true; destroying = true;
pjsua_destroy(); pjsua_destroy();
stop_pa();
if (answered)
kill(ppid, SIGINT);
exit(1); exit(1);
} }
} }
void start_pa() {
#ifdef PCM_FILE
char _wavbuf[50];
sprintf(_wavbuf, PCM_FILE, "recv");
wave_recv = open(_wavbuf, O_WRONLY | O_CREAT, 00644);
sprintf(_wavbuf, PCM_FILE, "transmit");
wave_recv = open(_wavbuf, O_WRONLY | O_CREAT, 00644);
if (wave_recv <= 0 || wave_transmit <= 0)
error_exit("open wave files", 1);
#endif
#ifdef HAS_PULSE
pa_s1 = pa_simple_new(NULL, // Use the default server.
"dmodem", // Our application's name.
PA_STREAM_PLAYBACK,
NULL, // Use the default device.
"recv", // Description of our stream.
&pa_ss, // Our sample format.
NULL, // Use default channel map
NULL, // Use default buffering attributes.
NULL // Ignore error code.
);
pa_s2 = pa_simple_new(NULL, // Use the default server.
"dmodem", // Our application's name.
PA_STREAM_PLAYBACK,
NULL, // Use the default device.
"transmit", // Description of our stream.
&pa_ss, // Our sample format.
NULL, // Use default channel map
NULL, // Use default buffering attributes.
NULL // Ignore error code.
);
if (!pa_s1 || !pa_s2)
error_exit("pulseaudio", 1);
#endif
}
void stop_pa() {
#ifdef HAS_PULSE
if(pa_s1)
pa_simple_free(pa_s1);
if(pa_s2)
pa_simple_free(pa_s2);
pa_s1 = 0;
pa_s1 = 0;
#endif
#ifdef PCM_FILE
if (wave_recv >= 0)
close(wave_recv);
if (wave_transmit >= 0)
close(wave_transmit);
wave_recv = -1;
wave_transmit = -1;
#endif
}
static pj_status_t dmodem_put_frame(pjmedia_port *this_port, pjmedia_frame *frame) { static pj_status_t dmodem_put_frame(pjmedia_port *this_port, pjmedia_frame *frame) {
struct dmodem *sm = (struct dmodem *)this_port; struct dmodem *sm = (struct dmodem *)this_port;
int len; int len;
if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) { if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
#ifdef HAS_PULSE
pa_simple_write(pa_s1, frame->buf, frame->size, NULL);
#endif
#ifdef PCM_FILE
write(wave_recv, frame->buf, frame->size);
#endif
if ((len=write(sm->sock, frame->buf, frame->size)) != frame->size) { if ((len=write(sm->sock, frame->buf, frame->size)) != frame->size) {
error_exit("error writing frame",0); error_exit("error writing frame",0);
} }
@ -78,12 +168,20 @@ static pj_status_t dmodem_get_frame(pjmedia_port *this_port, pjmedia_frame *fram
frame->timestamp.u64 = sm->timestamp.u64; frame->timestamp.u64 = sm->timestamp.u64;
frame->type = PJMEDIA_FRAME_TYPE_AUDIO; frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
sm->timestamp.u64 += PJMEDIA_PIA_SPF(&this_port->info); sm->timestamp.u64 += PJMEDIA_PIA_SPF(&this_port->info);
#ifdef HAS_PULSE
pa_simple_write(pa_s2, frame->buf, frame->size, NULL);
#endif
#ifdef PCM_FILE
write(wave_transmit, frame->buf, frame->size);
#endif
return PJ_SUCCESS; return PJ_SUCCESS;
} }
static pj_status_t dmodem_on_destroy(pjmedia_port *this_port) { static pj_status_t dmodem_on_destroy(pjmedia_port *this_port) {
printf("destroy\n"); printf("destroy\n");
if (answered)
kill(ppid, SIGINT);
exit(-1); exit(-1);
} }
@ -99,15 +197,15 @@ static void on_call_state(pjsua_call_id call_id, pjsip_event *e) {
ci.state_text.ptr)); ci.state_text.ptr));
if (ci.state == PJSIP_INV_STATE_DISCONNECTED) { if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
if(mode != DMODEM_RING_DETECT_MODE) { close(port.sock);
close(port.sock); if (!destroying) {
if (!destroying) { destroying = true;
destroying = true; pjsua_destroy();
pjsua_destroy(); stop_pa();
exit(0); if (answered)
} kill(ppid, SIGINT);
exit(0);
} }
ringing = 0;
} }
} }
@ -117,7 +215,12 @@ static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_r
char* tmp = malloc(pj_strlen(&ci.remote_contact)+1); char* tmp = malloc(pj_strlen(&ci.remote_contact)+1);
strcpy(tmp, ci.remote_contact.ptr); strcpy(tmp, ci.remote_contact.ptr);
tmp[pj_strlen(&ci.remote_contact)] = '\0'; tmp[pj_strlen(&ci.remote_contact)] = '\0';
printf("Incoming call from: %s\n", tmp); if (answered) {
PJ_LOG(2,(__FILE__, "Incoming call rejected from: %s", tmp));
pjsua_call_hangup(call_id, 0u, NULL, NULL);
return;
}
PJ_LOG(2,(__FILE__, "Incoming call from: %s", tmp));
free(tmp); free(tmp);
incoming = call_id; incoming = call_id;
ringing = 1; ringing = 1;
@ -144,28 +247,54 @@ static void on_call_media_state(pjsua_call_id call_id) {
} }
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
pjsua_acc_id acc_id; pjsua_acc_id acc_id;
pj_status_t status; pj_status_t status;
if (argc != 3) { if (argc != 5) {
return -1; return -1;
} }
ringing = 0; ppid = atoi(argv[3]);
if(!strncmp(argv[1], "++", 2)) { char* _modemid_tmp = argv[4];
for (size_t i = strlen(_modemid_tmp) - 1 ; ; i--) {
if (i < 0 || _modemid_tmp[i] < '0' || _modemid_tmp[i] > '9') {
_modemid_tmp += i+1;
break;
}
}
int sip_port = atoi(_modemid_tmp);
sip_port = sip_port >= 0 ? sip_port : 0;
sip_port += 5060;
sip_port = sip_port <= 65535 ? sip_port : 65535;
if(!strncmp(argv[1], "rr", 2)) {
mode = DMODEM_ANSWER_MODE; mode = DMODEM_ANSWER_MODE;
} else if(!strncmp(argv[1], "rr", 2)) {
mode = DMODEM_RING_DETECT_MODE;
} }
signal(SIGPIPE,SIG_IGN); signal(SIGPIPE,SIG_IGN);
char *dialstr = argv[1]; char *dialstr = argv[1];
char *sip_user = "dialupuser"; int has_sip_user = 1;
char *sip_domain = "192.168.1.2"; const char *_sip_user = getenv("SIP_LOGIN");
char *sip_pass = "pppasswdModem1"; char *sip_user = NULL;
printf("sip data: user: %s, passwd: %s, server: %s\nMODE: %d\n", sip_user, sip_pass, sip_domain, mode); if (!_sip_user) {
has_sip_user = 0;
_sip_user = "placeholder:placeholder@placeholder";
}
sip_user = malloc(strlen(_sip_user) + 1);
strcpy(sip_user, _sip_user);
char *sip_domain = strchr(sip_user,'@');
if (!sip_domain) {
return -1;
}
*sip_domain++ = '\0';
char *sip_pass = strchr(sip_user,':');
if (!sip_pass) {
return -1;
}
*sip_pass++ = '\0';
status = pjsua_create(); status = pjsua_create();
if (status != PJ_SUCCESS) error_exit("Error in pjsua_create()", status); if (status != PJ_SUCCESS) error_exit("Error in pjsua_create()", status);
if (!has_sip_user)
PJ_LOG(2,(__FILE__, "SIP_LOGIN is empty, no registration will be attempted"));
/* Init pjsua */ /* Init pjsua */
{ {
@ -177,19 +306,23 @@ int main(int argc, char *argv[]) {
cfg.cb.on_call_media_state = &on_call_media_state; cfg.cb.on_call_media_state = &on_call_media_state;
cfg.cb.on_call_state = &on_call_state; cfg.cb.on_call_state = &on_call_state;
if(mode == DMODEM_RING_DETECT_MODE) { if(mode == DMODEM_ANSWER_MODE) {
cfg.cb.on_incoming_call = &on_incoming_call; cfg.cb.on_incoming_call = &on_incoming_call;
} }
pjsua_logging_config_default(&log_cfg); pjsua_logging_config_default(&log_cfg);
log_cfg.console_level = 4; // log_cfg.console_level = 4;
log_cfg.console_level = 2;
pjsua_media_config_default(&med_cfg); pjsua_media_config_default(&med_cfg);
med_cfg.no_vad = true; med_cfg.no_vad = true;
med_cfg.ec_tail_len = 0; med_cfg.ec_tail_len = 0;
med_cfg.jb_max = 2000; med_cfg.jb_max = -1;
// med_cfg.jb_init = 200; med_cfg.jb_init = -1;
med_cfg.audio_frame_ptime = 5; med_cfg.audio_frame_ptime = 10;
med_cfg.quality = 10;
med_cfg.enable_ice = PJ_FALSE;
med_cfg.enable_turn = PJ_FALSE;
status = pjsua_init(&cfg, &log_cfg, &med_cfg); status = pjsua_init(&cfg, &log_cfg, &med_cfg);
if (status != PJ_SUCCESS) error_exit("Error in pjsua_init()", status); if (status != PJ_SUCCESS) error_exit("Error in pjsua_init()", status);
@ -197,33 +330,38 @@ int main(int argc, char *argv[]) {
pjsua_set_ec(0,0); // maybe? pjsua_set_ec(0,0); // maybe?
pjsua_set_null_snd_dev(); pjsua_set_null_snd_dev();
/* g711 only */ /* g711 only */
pjsua_codec_info codecs[32]; pjsua_codec_info codecs[32];
unsigned count = sizeof(codecs)/sizeof(*codecs); unsigned count = sizeof(codecs)/sizeof(*codecs);
pjsua_enum_codecs(codecs,&count); pjsua_enum_codecs(codecs,&count);
for (int i=0; i<count; i++) { for (int i=0; i<count; i++) {
int pri = 0; int pri = 0;
if (pj_strcmp2(&codecs[i].codec_id,"PCMU/8000/1") == 0) { if (pj_strcmp2(&codecs[i].codec_id,"PCMA/8000/1") == 0) {
pri = 1; pri = 1;
} else if (pj_strcmp2(&codecs[i].codec_id,"PCMA/8000/1") == 0) { } else if (pj_strcmp2(&codecs[i].codec_id,"PCMU/8000/1") == 0) {
pri = 2; pri = 2;
} }
pjsua_codec_set_priority(&codecs[i].codec_id, pri); pjsua_codec_set_priority(&codecs[i].codec_id, pri);
// printf("codec: %s %d\n",pj_strbuf(&codecs[i].codec_id),pri); // printf("codec: %s %d\n",pj_strbuf(&codecs[i].codec_id),pri);
} }
pjsua_transport_id transport_id;
/* Add UDP transport. */ /* Add UDP transport. */
{ {
pjsua_transport_config cfg; pjsua_transport_config cfg;
pjsua_transport_config_default(&cfg); pjsua_transport_config_default(&cfg);
cfg.port = 5060; if (mode)
status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &cfg, NULL); cfg.port = sip_port;
if (getenv("PJSIP_IPV6"))
status = pjsua_transport_create(PJSIP_TRANSPORT_UDP6, &cfg, &transport_id);
else
status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &cfg, &transport_id);
if (status != PJ_SUCCESS) error_exit("Error creating transport", status); if (status != PJ_SUCCESS) error_exit("Error creating transport", status);
} }
char buf[384]; char buf[384];
printf("Initializing pool\n"); //printf("Initializing pool\n");
pj_caching_pool cp; pj_caching_pool cp;
pj_caching_pool_init(&cp, NULL, 1024*1024); pj_caching_pool_init(&cp, NULL, 1024*1024);
pool = pj_pool_create(&cp.factory, "pool1", 4000, 4000, NULL); pool = pj_pool_create(&cp.factory, "pool1", 4000, 4000, NULL);
@ -232,8 +370,6 @@ int main(int argc, char *argv[]) {
memset(&port,0,sizeof(port)); memset(&port,0,sizeof(port));
port.sock = atoi(argv[2]); // inherited from parent port.sock = atoi(argv[2]); // inherited from parent
if(mode == DMODEM_RING_DETECT_MODE)
port.sock = NULL;
pjmedia_port_info_init(&port.base.info, &name, SIGNATURE, 9600, 1, 16, 192); pjmedia_port_info_init(&port.base.info, &name, SIGNATURE, 9600, 1, 16, 192);
port.base.put_frame = dmodem_put_frame; port.base.put_frame = dmodem_put_frame;
port.base.get_frame = dmodem_get_frame; port.base.get_frame = dmodem_get_frame;
@ -245,6 +381,7 @@ int main(int argc, char *argv[]) {
status = pjsua_start(); status = pjsua_start();
if (status != PJ_SUCCESS) error_exit("Error starting pjsua", status); if (status != PJ_SUCCESS) error_exit("Error starting pjsua", status);
if (has_sip_user)
{ {
pjsua_acc_config cfg; pjsua_acc_config cfg;
pjsua_acc_config_default(&cfg); pjsua_acc_config_default(&cfg);
@ -263,32 +400,36 @@ int main(int argc, char *argv[]) {
status = pjsua_acc_add(&cfg, PJ_TRUE, &acc_id); status = pjsua_acc_add(&cfg, PJ_TRUE, &acc_id);
if (status != PJ_SUCCESS) error_exit("Error adding account", status); if (status != PJ_SUCCESS) error_exit("Error adding account", status);
} }
else
{
status == pjsua_acc_add_local(transport_id, 1, &acc_id);
if (status != PJ_SUCCESS) error_exit("Error adding local account", status);
}
pjsua_get_var()->tpdata[transport_id].has_bound_addr = PJ_TRUE;
if (getenv("PJSIP_IPV6"))
pjsua_get_var()->acc[acc_id].cfg.ipv6_media_use = PJSUA_IPV6_ENABLED;
start_pa();
if(mode == DMODEM_DIAL_MODE) { if(mode == DMODEM_DIAL_MODE) {
snprintf(buf,sizeof(buf),"sip:%s@%s",dialstr,sip_domain); if (has_sip_user)
printf("calling %s\n",buf); snprintf(buf,sizeof(buf),"sip:%s@%s",dialstr,sip_domain);
else
snprintf(buf,sizeof(buf),"sip:%s",dialstr);
PJ_LOG(2,(__FILE__, "calling %s\n",buf));
pj_str_t uri = pj_str(buf); pj_str_t uri = pj_str(buf);
pjsua_call_id callid; pjsua_call_id callid;
status = pjsua_call_make_call(acc_id, &uri, 0, NULL, NULL, &callid); status = pjsua_call_make_call(acc_id, &uri, 0, NULL, NULL, &callid);
if (status != PJ_SUCCESS) error_exit("Error making call", status); if (status != PJ_SUCCESS) error_exit("Error making call", status);
} }
if(mode == DMODEM_ANSWER_MODE) {
pjsua_call_id id; struct timespec ts = {1, 0};
char* cid = strrchr(argv[1], '+');
id = atoi(cid);
status = pjsua_call_answer(id, 200, NULL, NULL);
if (status != PJ_SUCCESS) error_exit("Error answering call", status);
}
struct timespec ts = {100, 0};
if(mode == DMODEM_RING_DETECT_MODE)
ts.tv_sec = 1;
time_t now = time(NULL);
while(1) { while(1) {
if(mode == DMODEM_RING_DETECT_MODE) { if(mode == DMODEM_ANSWER_MODE) {
if(ringing) { if(ringing) {
char cid[11]; status = pjsua_call_answer(incoming, 200, NULL, NULL);
snprintf(cid, 10, "%d", incoming); if (status != PJ_SUCCESS) error_exit("Error answering call", status);
write(atoi(argv[2]), cid, strlen(cid)); ringing = 0;
answered = 1;
} }
} }
nanosleep(&ts,NULL); nanosleep(&ts,NULL);

419
mm.py Normal file
View file

@ -0,0 +1,419 @@
import subprocess
import threading
import select
import time
import tty
import pathlib
import re
import os
import signal
import ipaddress
import io
import pty
import logging
import pwd
import ctypes
import argparse
from typing import Union, Dict, List, Tuple, Callable, Any
logging.basicConfig(level=logging.DEBUG,format='%(threadName)-5s - %(levelname)s - %(message)s')
logger = logging.getLogger()
PJSIP_V6 = False
# exports PJSIP_IPV6=1 to d-modem
# uses ipv6 sip transport to preserve address space
# utilize ipv4 over v6 next hop so that no v4 is required in the container
PTY_LOC = pathlib.Path("/tmp")
RUN_AS = "nobody"
get_pty = lambda n: PTY_LOC / f"ttySL{n}"
MODEMD = "./slmodemd/slmodemd"
D_MODEM = './d-modem.nopulse'
IP4RANGE = ipaddress.ip_network("10.0.0.0/27")
IP6RANGE = ipaddress.ip_network("fd00::/64")
PRODUCTION = False
# Do we really need that many?
# modem_configs: Dict[int, List[str]] = {
# **{i: (["AT+MS=92,1" ], lambda: PPPProc) for i in range(00, 05)},
# **{i: (["AT+MS=90,1" ], lambda: PPPProc) for i in range(05, 10)},
# **{i: (["AT+MS=34,1" ], lambda: PPPProc) for i in range(10, 15)},
# **{i: (["AT+MS=132,1"], lambda: PPPProc) for i in range(15, 20)},
# **{i: (["AT+MS=32,1" ], lambda: PPPProc) for i in range(20, 25)}, # broken?
# **{i: (["AT+MS=122,1"], lambda: PPPProc) for i in range(25, 30)},
# **{i: (["AT+MS=22,1" ], lambda: PPPProc) for i in range(30, 35)},
# **{i: (["AT+MS=212,1"], lambda: PPPProc) for i in range(35, 40)},
# **{i: (["AT+MS=23,1" ], lambda: PPPProc) for i in range(40, 45)},
# **{i: (["AT+MS=21,1" ], lambda: PPPProc) for i in range(45, 50)}, # broken?
# **{i: (["AT+MS=103,1"], lambda: PPPProc) for i in range(50, 55)},
# }
modem_configs: Dict[int, Tuple[List[str], Callable]] = {
**{i: (["AT+MS=90,1"], lambda: PPPProc) for i in range(0, 1)},
**{i: (["AT+MS=34,1"], lambda: PPPProc) for i in range(1, 2)},
**{i: (["AT+MS=32,1"], lambda: PPPProc) for i in range(2, 3)},
**{i: (["AT+MS=22,1"], lambda: PPPProc) for i in range(3, 5)},
**{i: (["AT+MS=23,1"], lambda: PPPProc) for i in range(5, 7)},
}
assert all(i >= 0 for i in modem_configs)
class BaseProc:
def __init__(self) -> None:
self.proc: subprocess.Popen = None
def proc(self) -> subprocess.Popen:
return self.proc
def terminate(self, *args, **kwargs):
return self.proc.terminate(*args, **kwargs)
def wait(self, *args, **kwargs):
return self.proc.wait(*args, **kwargs)
def poll(self, *args, **kwargs):
return self.proc.poll(*args, **kwargs)
def send_signal(self, *args, **kwargs):
return self.proc.send_signal(*args, **kwargs)
class ShellProc(BaseProc):
''' danger! '''
def __init__(self, ptyr: io.BufferedReader, ptyw: io.BufferedWriter, speed: int, no: int,
pty_path: pathlib.Path, ip: Tuple[ipaddress.IPv4Address, ipaddress.IPv6Address]) -> None:
assert not PRODUCTION
self.proc = subprocess.Popen(
['bash', '-c', 'stty sane;exec bash'],
stdin=ptyr,
stdout=ptyw,
stderr=ptyw,
preexec_fn=os.setsid,
env=dict(os.environ, TERM='vt100')
)
class PPPProc(BaseProc):
def __init__(self, ptyr: io.BufferedReader, ptyw: io.BufferedWriter, speed: int, no: int,
pty_path: pathlib.Path, ip: Tuple[ipaddress.IPv4Address, ipaddress.IPv6Address]) -> None:
self.no = no
self.ip = ip
ptyw.write((
f"Welcome to D-Modem\r\nExample pppd launch options:\r\n"
f" pppd /dev/ttyACM0 {speed} defaultroute persist noproxyarp noauth modem nodetach\r\n"
f"Your ipv6 address is {ip[1]}.\r\n"
f"Please add Address={ip[1]}/{IP6RANGE.prefixlen} Gateway={IP6RANGE[1]}\r\n"
"to your ppp network device.\r\n"
"Enjoy!\r\n\r\n\r\n"
).encode('utf-8'))
ll_ident = lambda x: ipaddress.IPv6Address(int(x) & int(ipaddress.IPv6Address(2**(128-IP6RANGE.prefixlen)-1)))
self.proc = PopenComm(
[
'pppd', str(pty_path.resolve()), str(speed), 'noproxyarp', 'passive', 'noauth',
'unit', str(no), 'modem', 'nodefaultroute', 'nodetach', f"{IP4RANGE[0]}:{ip[0]}",
'+ipv6', 'ipv6', f"{ll_ident(IP6RANGE[1])},{ll_ident(ip[1])}",
'ipcp-max-configure', '100', 'ipcp-max-failure', '100', 'ipcp-restart', '15',
'ipv6cp-max-configure', '100', 'ipv6cp-max-failure', '100', 'ipv6cp-restart', '15'
]
)
def wait(self, *args, **kwargs):
while (lines := self.proc.readline()) is not None:
for line in lines:
logger.info(f"pppd: {line}")
if 'remote LL address' in line:
try:
subprocess.run(
[
'ip', '-6', 'a', 'replace', 'dev', f"ppp{self.no}",
f"{IP6RANGE[1]}/128", 'peer', f"{self.ip[1]}/128"
],
stdin=subprocess.DEVNULL,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL
)
logger.info(f"added v6 route to ppp{self.no}")
except Exception:
logger.exception(f"adding v6 route to ppp{self.no}")
super().wait(*args, **kwargs)
class ModemManagerError(Exception):
pass
class ReadLineException(ModemManagerError):
pass
class PPPDDead(ModemManagerError):
pass
class CarrierLost(ModemManagerError):
pass
class ModemManager:
terminating = False
''' lol not that modem_manager '''
def __init__(self):
self.modems = {no: Modem(no, config) for no, config in modem_configs.items()}
self.ip_used4 = {IP4RANGE[0]}
self.ip_used6 = {IP6RANGE[0], IP6RANGE[1]}
class PopenComm(subprocess.Popen):
def __init__(self, *args, **kwargs) -> None:
try:
self._popen_comm_master, self._popen_comm_slave = pty.openpty()
os.set_blocking(self._popen_comm_master, False)
self._popen_comm_r = open(self._popen_comm_master, "rb", buffering=0)
kwargs['stdin'] = kwargs['stdout'] = kwargs['stderr'] = self._popen_comm_master
super().__init__(*args, **kwargs)
except Exception:
self._popen_comm_cleanup()
raise
def w(self):
try:
super().wait(timeout=None)
except Exception:
logger.exception("Popen wait")
self._popen_comm_cleanup()
self.wtr = threading.Thread(target=w, args=(self,))
self.wtr.start()
def wait(self, timeout=None) -> int:
if timeout is not None:
return super().wait(timeout=timeout)
self.wtr.join()
if self.poll() is None:
return super().wait(timeout=None)
return self.returncode
def _popen_comm_cleanup(self) -> None:
try:
os.close(self._popen_comm_slave)
except Exception:
pass
try:
self._popen_comm_r.close()
except Exception:
pass
try:
os.close(self._popen_comm_master)
except Exception:
pass
logger.info(f"{self} pty pair closed")
def readline(self) -> Union[List[str], None]:
''' blocks '''
try:
select.select([self._popen_comm_master], list(), list())
read = self._popen_comm_r.read().decode('utf-8', errors='replace')
except (OSError, ValueError):
return None
except Exception:
logger.exception("readline")
return None
if not read:
return None
got = read.split('\n')
if not got[-1]:
got.pop(-1)
return got
class IP_Request:
lock = {4: threading.Lock(), 6: threading.Lock()}
def __init__(self, version: int) -> None:
assert version in {4, 6}
self.v = version
self.addr = None
self.used = modem_manger.ip_used4 if self.v == 4 else modem_manger.ip_used6
def __enter__(self):
with self.lock[self.v]:
for address in (IP4RANGE if self.v == 4 else IP6RANGE):
if address in self.used:
continue
else:
self.addr = address
self.used.add(address)
logger.info(f"ipv{self.v} lease: {str(self.addr)}")
return address
raise RuntimeError(f"error: ipv{self.v} address exhausted")
def __exit__(self, *_):
if self.addr:
with self.lock[self.v]:
self.used.remove(self.addr)
logger.info(f"ipv{self.v} return: {str(self.addr)}")
class Modem:
def __init__(self, no: int, config: Tuple[List[str], Callable]):
self.no = no
self.readline_quit = False
self.readlinebuf = []
self.modem_proc: Union[subprocess.Popen, None] = None
self.ppp_proc: Union[subprocess.Popen, None] = None
self.modem_cmd, get_ppp_func = config
self.ppp_func = get_ppp_func()
self.modem_cmd = [*self.modem_cmd, "ATA"]
self.pty_path = get_pty(self.no)
if os.path.exists(self.pty_path):
logger.warning(f"pty link exists {str(self.pty_path)}")
self.pty_path.unlink()
self.modem_ctl: threading.Thread = threading.Thread(target=self._start_modem_ctl, name=f"md{no:03d}")
self.ppp_ctl: threading.Thread = threading.Thread(target=self._start_ppp_ctl, name=f"pp{no:03d}")
self.modem_ctl.start()
self.ppp_ctl.start()
def _util_readline(self, dev: io.BufferedReader, timeout: float) -> bytes:
readlinebuf = self.readlinebuf
readlinebuf.clear()
time_remaining = timeout
while True:
read_ready, _, _ = select.select([dev], list(), list(), 0.1)
if self.readline_quit:
self.readline_quit = False
raise ReadLineException("process exit")
if not read_ready:
time_remaining -= 0.1
if time_remaining < 0:
break
continue
readlinebuf.append(dev.read(1))
if readlinebuf[-1] == b'\n':
return b''.join(readlinebuf)
return b''.join(readlinebuf)
def _start_modem_ctl(self) -> None:
while not ModemManager.terminating:
try:
if os.geteuid() == 0:
pw_record = pwd.getpwnam(RUN_AS)
uid, gid = pw_record.pw_uid, pw_record.pw_gid
def demote():
PR_SET_NO_NEW_PRIVS = 38
PR_CAP_AMBIENT = 47
PR_CAP_AMBIENT_CLEAR_ALL = 4
PR_GET_SECUREBITS = 27
PR_SET_SECUREBITS = 28
libc = ctypes.CDLL('libc.so.6')
libc.prctl.restype = ctypes.c_int
assert libc.prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == 0
assert libc.prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0) == 0
assert libc.prctl(PR_SET_SECUREBITS, 0x2f) == 0
# SECBIT_KEEP_CAPS_LOCKED | SECBIT_NO_SETUID_FIXUP | SECBIT_NO_SETUID_FIXUP_LOCKED | SECBIT_NOROOT | SECBIT_NOROOT_LOCKED
assert libc.prctl(PR_GET_SECUREBITS) == 0x2f
os.setgroups([])
os.setresgid(gid, gid, gid)
os.setresuid(uid, uid, uid)
os.chdir(os.getcwd())
preexec_func = demote
env = {
'USER': pw_record.pw_name,
'LOGNAME': pw_record.pw_name,
'HOME': '/',
'PATH': os.environ['PATH'],
}
else:
preexec_func = lambda: None
env = dict(os.environ)
logger.warning("d-modem is running as current user")
assert not PRODUCTION
if PJSIP_V6:
env['PJSIP_IPV6'] = '1'
else:
if 'PJSIP_IPV6' in env:
env.pop('PJSIP_IPV6')
self.modem_proc = PopenComm(
[MODEMD, '-e', D_MODEM, str(self.pty_path)],
preexec_fn=preexec_func,
env = env
)
while (lines := self.modem_proc.readline()) is not None:
for line in lines:
logger.info(f"slmodemd: {line}")
self.modem_proc.wait()
except Exception:
logger.exception("unknown error")
try:
self.readline_quit = True
if self.ppp_proc:
self.ppp_proc.terminate()
self.ppp_proc.wait()
self.ppp_proc = None
except Exception as err:
logger.exception("unknown error")
self.ppp_ctl.join()
def _start_ppp_ctl(self) -> None:
time.sleep(1)
while not ModemManager.terminating:
commands = self.modem_cmd.copy()
try:
with open(self.pty_path.resolve(), "rb", buffering=0) as ptyr, open(self.pty_path.resolve(), "wb", buffering=0) as ptyw:
tty.setraw(ptyr)
tty.setraw(ptyw)
while True:
while (got := self._util_readline(ptyr, 0.3).decode('utf-8', errors='replace')):
logger.debug(f"from pty: {got=}")
if got:
if (match := re.match(r'CONNECT ([0-9]+)', got)) and not self.ppp_proc:
speed = int(match.groups()[0])
assert speed > 0
logger.info(f"connection {speed=}, start subprocess")
ptyw.write(f"Remote speed {speed}\r\n".encode('utf-8'))
try:
with IP_Request(4) as ip4, IP_Request(6) as ip6:
self.ppp_proc = self.ppp_func(ptyr, ptyw, speed, self.no, self.pty_path, (ip4, ip6))
self.ppp_proc.wait()
except Exception:
logger.exception("unknown subprocess error")
self.modem_proc.send_signal(signal.SIGINT)
self.modem_proc.wait()
raise PPPDDead
elif got == 'NO CARRIER\r\n':
self._util_readline(ptyr, 2)
raise CarrierLost
if commands:
cmd = commands.pop(0)
ptyw.write(f"{cmd}\n\r".encode('ascii'))
logger.info(f"command: {cmd}")
ptyw.flush()
time.sleep(0.1)
except ModemManagerError as err:
logger.info(f"pty detach: {repr(err)}")
time.sleep(0.1)
except Exception:
logger.exception("unknown error")
time.sleep(2)
if __name__ == "__main__":
abspath=os.path.abspath(__file__)
abspath=os.path.dirname(abspath)
os.chdir(abspath)
parser = argparse.ArgumentParser(description='ModemManager.py')
parser.add_argument('-4', '--ipv4', type=str, default=str(IP4RANGE), help='ipv4 subnet')
parser.add_argument('-6', '--ipv6', type=str, default=str(IP6RANGE), help='ipv6 subnet')
parser.add_argument('-m', '--modemd', type=str, default=str(MODEMD), help='modemd location')
parser.add_argument('-d', '--dmodem', type=str, default=str(D_MODEM), help='d-modem location')
parser.add_argument('-p', '--pty', type=str, default=str(PTY_LOC), help='pty link location')
parser.add_argument('-u', '--user', type=str, default=str(RUN_AS), help='run as user')
parser.add_argument('-s', '--production', action='store_true', help='enable strict checks')
parser.add_argument('--pjsip6', action='store_true', help='pjsip force v6')
args = parser.parse_args()
def g(x: str, y: Any) -> None:
globals()[x] = y
g('IP4RANGE', ipaddress.ip_network(args.ipv4))
g('IP6RANGE', ipaddress.ip_network(args.ipv6))
g('MODEMD', args.modemd)
g('D_MODEM', args.dmodem)
g('PTY_LOC', pathlib.Path(args.pty))
g('RUN_AS', args.user)
g('PRODUCTION', args.production)
g('PJSIP_V6', args.pjsip6)
if args.production:
logger.setLevel(logging.INFO)
modem_manger = ModemManager()
def sighandler(signum, _frame):
if ModemManager.terminating:
logger.error(f"received signal {signum} while terminating")
else:
ModemManager.terminating = True
logger.warning(f"terminate on signal {signum}")
for modem in modem_manger.modems.values():
modem.modem_proc.terminate()
for sig in (signal.SIGTERM, signal.SIGHUP, signal.SIGINT, signal.SIGABRT):
signal.signal(sig, sighandler)
for modem in modem_manger.modems.values():
modem.modem_ctl.join()

View file

@ -24,7 +24,8 @@ endif
RM:= rm -f RM:= rm -f
CFLAGS+= -Wall -g -O -I. -DCONFIG_DEBUG_MODEM # CFLAGS+= -Wall -g -O -I. -DCONFIG_DEBUG_MODEM
CFLAGS+= -Wall -O1 -I. -DCONFIG_DEBUG_MODEM
modem-objs:= \ modem-objs:= \
modem.o modem_datafile.o modem_at.o modem_timer.o \ modem.o modem_datafile.o modem_at.o modem_timer.o \

View file

@ -70,6 +70,7 @@
#define MODEM_DBG(fmt,arg...) dprintf("%s: " fmt , m->name , ##arg) #define MODEM_DBG(fmt,arg...) dprintf("%s: " fmt , m->name , ##arg)
#define MODEM_ERR(fmt,arg...) eprintf("%s: " fmt , m->name , ##arg) #define MODEM_ERR(fmt,arg...) eprintf("%s: " fmt , m->name , ##arg)
short _MODEM_DO_ANSWER = 0;
/* external symbols */ /* external symbols */
extern int process_at_command(struct modem *m, char *buf); extern int process_at_command(struct modem *m, char *buf);
extern void *dp_runtime_create(struct modem *m); extern void *dp_runtime_create(struct modem *m);
@ -1567,6 +1568,7 @@ int modem_recv_from_tty(struct modem *m, char *buf, int n)
int modem_answer(struct modem *m) int modem_answer(struct modem *m)
{ {
_MODEM_DO_ANSWER = 1;
MODEM_DBG("modem answer...\n"); MODEM_DBG("modem answer...\n");
if ( m->dp ) { if ( m->dp ) {
MODEM_ERR("dp %d is already exists.\n", m->dp->id); MODEM_ERR("dp %d is already exists.\n", m->dp->id);
@ -1613,6 +1615,7 @@ static int modem_dial_start(struct modem *m)
int modem_dial(struct modem *m) int modem_dial(struct modem *m)
{ {
_MODEM_DO_ANSWER = 0;
int ret; int ret;
MODEM_DBG("modem dial: %s...\n", m->dial_string); MODEM_DBG("modem dial: %s...\n", m->dial_string);
m->dp_requested = 0; m->dp_requested = 0;

View file

@ -456,5 +456,7 @@ extern void modem_update_config(struct modem *m, struct modem_config *cfg);
#define MODEM_DP(m) ((m)->sregs[SREG_DP]) #define MODEM_DP(m) ((m)->sregs[SREG_DP])
#define MODEM_AUTOMODE(m) ((m)->sregs[SREG_AUTOMODE]) #define MODEM_AUTOMODE(m) ((m)->sregs[SREG_AUTOMODE])
extern short _MODEM_DO_ANSWER;
#endif /* __MODEM_H__ */ #endif /* __MODEM_H__ */

View file

@ -68,10 +68,10 @@ extern unsigned int modem_debug_logging;
/* config parameters */ /* config parameters */
const char *modem_dev_name = NULL; const char *modem_dev_name = NULL;
const char *modem_default_dev_name = "/dev/slamr0"; const char *modem_default_dev_name = "/tmp/ttySL0";
const char *modem_alsa_dev_name = "modem:1"; const char *modem_alsa_dev_name = "modem:1";
const char *modem_exec = NULL; const char *modem_exec = NULL;
unsigned int need_realtime = 1; unsigned int need_realtime = 0;
#ifdef MODEM_CONFIG_RING_DETECTOR #ifdef MODEM_CONFIG_RING_DETECTOR
unsigned int ring_detector = 0; unsigned int ring_detector = 0;
#endif #endif
@ -125,7 +125,7 @@ static struct opt {
{'s',"shortbuffer","use short buffer (4 periods length)"}, {'s',"shortbuffer","use short buffer (4 periods length)"},
{'d',"debug","debug level (developers only, for ./sl...)",OPTIONAL,INTEGER,"0"}, {'d',"debug","debug level (developers only, for ./sl...)",OPTIONAL,INTEGER,"0"},
{'l',"log","logging mode",OPTIONAL,INTEGER,"5"}, {'l',"log","logging mode",OPTIONAL,INTEGER,"5"},
{'e',"exec","path to external application that transmits audio over the socket (required)",MANDATORY,STRING,""}, {'e',"exec","path to external application that transmits audio over the socket (required)",MANDATORY,STRING,"./d-modem"},
{} {}
}; };

View file

@ -87,7 +87,7 @@
#define DBG(fmt,args...) dprintf("main: " fmt, ##args) #define DBG(fmt,args...) dprintf("main: " fmt, ##args)
#define SLMODEMD_USER "nobody" //#define SLMODEMD_USER "nobody"
#define LOCKED_MEM_MIN_KB (8UL * 1024) #define LOCKED_MEM_MIN_KB (8UL * 1024)
#define LOCKED_MEM_MIN (LOCKED_MEM_MIN_KB * 1024) #define LOCKED_MEM_MIN (LOCKED_MEM_MIN_KB * 1024)
@ -217,19 +217,19 @@ static int alsa_device_setup(struct device_struct *dev, const char *dev_name)
ret = snd_pcm_open(&dev->phandle, dev_name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); ret = snd_pcm_open(&dev->phandle, dev_name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
if(ret < 0) { if(ret < 0) {
ERR("alsa setup: cannot open playback device '%s': %s\n", ERR("alsa setup: cannot open playback device '%s': %s\n",
dev_name, snd_strerror(ret)); dev_name, snd_strerror(ret));
return -1; return -1;
} }
ret = snd_pcm_open(&dev->chandle, dev_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); ret = snd_pcm_open(&dev->chandle, dev_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
if(ret < 0) { if(ret < 0) {
ERR("alsa setup: cannot open playback device '%s': %s\n", ERR("alsa setup: cannot open playback device '%s': %s\n",
dev_name, snd_strerror(ret)); dev_name, snd_strerror(ret));
return -1; return -1;
} }
ret = snd_pcm_poll_descriptors(dev->chandle, &pfd, 1); ret = snd_pcm_poll_descriptors(dev->chandle, &pfd, 1);
if(ret <= 0) { if(ret <= 0) {
ERR("alsa setup: cannot get poll descriptors of '%s': %s\n", ERR("alsa setup: cannot get poll descriptors of '%s': %s\n",
dev_name, snd_strerror(ret)); dev_name, snd_strerror(ret));
return -1; return -1;
} }
dev->fd = pfd.fd; dev->fd = pfd.fd;
@ -313,7 +313,7 @@ static int alsa_device_write(struct device_struct *dev, const char *buf, int cou
if (ret == -EAGAIN) if (ret == -EAGAIN)
continue; continue;
if (ret == -EPIPE) { if (ret == -EPIPE) {
ret = alsa_xrun_recovery(dev); ret = alsa_xrun_recovery(dev);
} }
written = ret; written = ret;
break; break;
@ -373,7 +373,7 @@ static int setup_stream(snd_pcm_t *handle, struct modem *m, const char *stream_n
ERR("cannot set format for %s: %s\n", stream_name, snd_strerror(err)); ERR("cannot set format for %s: %s\n", stream_name, snd_strerror(err));
return err; return err;
} }
err = snd_pcm_hw_params_set_channels(handle, hw_params, 1); err = snd_pcm_hw_params_set_channels(handle, hw_params, 1);
if (err < 0) { if (err < 0) {
ERR("cannot set channels for %s: %s\n", stream_name, snd_strerror(err)); ERR("cannot set channels for %s: %s\n", stream_name, snd_strerror(err));
return err; return err;
@ -386,7 +386,7 @@ static int setup_stream(snd_pcm_t *handle, struct modem *m, const char *stream_n
} }
if ( rrate != rate ) { if ( rrate != rate ) {
ERR("rate %d is not supported by %s (%d).\n", ERR("rate %d is not supported by %s (%d).\n",
rate, stream_name, rrate); rate, stream_name, rrate);
return -1; return -1;
} }
rsize = size = dev->period ; rsize = size = dev->period ;
@ -397,7 +397,7 @@ static int setup_stream(snd_pcm_t *handle, struct modem *m, const char *stream_n
} }
if ( rsize < size ) { if ( rsize < size ) {
ERR("period size %ld is not supported by %s (%ld).\n", ERR("period size %ld is not supported by %s (%ld).\n",
size, stream_name, rsize); size, stream_name, rsize);
return -1; return -1;
} }
rsize = size = use_short_buffer ? rsize * dev->buf_periods : rsize * 32; rsize = size = use_short_buffer ? rsize * dev->buf_periods : rsize * 32;
@ -408,7 +408,7 @@ static int setup_stream(snd_pcm_t *handle, struct modem *m, const char *stream_n
} }
if ( rsize != size ) { if ( rsize != size ) {
DBG("buffer size for %s is changed %ld -> %ld\n", DBG("buffer size for %s is changed %ld -> %ld\n",
stream_name, size, rsize); stream_name, size, rsize);
} }
err = snd_pcm_hw_params(handle, hw_params); err = snd_pcm_hw_params(handle, hw_params);
if (err < 0) { if (err < 0) {
@ -523,9 +523,9 @@ static int alsa_ioctl(struct modem *m, unsigned int cmd, unsigned long arg)
struct device_struct *dev = m->dev_data; struct device_struct *dev = m->dev_data;
DBG("alsa_ioctl: cmd %x, arg %lx...\n",cmd,arg); DBG("alsa_ioctl: cmd %x, arg %lx...\n",cmd,arg);
switch(cmd) { switch(cmd) {
case MDMCTL_CAPABILITIES: case MDMCTL_CAPABILITIES:
return -EINVAL; return -EINVAL;
case MDMCTL_HOOKSTATE: case MDMCTL_HOOKSTATE:
return (dev->hook_off_elem) ? return (dev->hook_off_elem) ?
snd_mixer_selem_set_playback_switch_all( snd_mixer_selem_set_playback_switch_all(
dev->hook_off_elem, dev->hook_off_elem,
@ -534,9 +534,9 @@ static int alsa_ioctl(struct modem *m, unsigned int cmd, unsigned long arg)
return (dev->speaker_elem) ? return (dev->speaker_elem) ?
snd_mixer_selem_set_playback_volume_all( snd_mixer_selem_set_playback_volume_all(
dev->speaker_elem, arg) : 0 ; dev->speaker_elem, arg) : 0 ;
case MDMCTL_CODECTYPE: case MDMCTL_CODECTYPE:
return CODEC_SILABS; return CODEC_SILABS;
case MDMCTL_IODELAY: case MDMCTL_IODELAY:
DBG("delay = %d\n", dev->delay); DBG("delay = %d\n", dev->delay);
return dev->delay; return dev->delay;
default: default:
@ -547,10 +547,10 @@ static int alsa_ioctl(struct modem *m, unsigned int cmd, unsigned long arg)
struct modem_driver alsa_modem_driver = { struct modem_driver alsa_modem_driver = {
.name = "alsa modem driver", .name = "alsa modem driver",
.start = alsa_start, .start = alsa_start,
.stop = alsa_stop, .stop = alsa_stop,
.ioctl = alsa_ioctl, .ioctl = alsa_ioctl,
}; };
@ -568,7 +568,7 @@ static int modemap_start (struct modem *m)
int ret; int ret;
DBG("modemap_start...\n"); DBG("modemap_start...\n");
dev->delay = 0; dev->delay = 0;
ret = ioctl(dev->fd,100000+MDMCTL_START,0); ret = ioctl(dev->fd,100000+MDMCTL_START,0);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = 192*2; ret = 192*2;
@ -586,7 +586,7 @@ static int modemap_stop (struct modem *m)
{ {
struct device_struct *dev = m->dev_data; struct device_struct *dev = m->dev_data;
DBG("modemap_stop...\n"); DBG("modemap_stop...\n");
return ioctl(dev->fd,100000+MDMCTL_STOP,0); return ioctl(dev->fd,100000+MDMCTL_STOP,0);
} }
static int modemap_ioctl(struct modem *m, unsigned int cmd, unsigned long arg) static int modemap_ioctl(struct modem *m, unsigned int cmd, unsigned long arg)
@ -607,10 +607,10 @@ static int modemap_ioctl(struct modem *m, unsigned int cmd, unsigned long arg)
struct modem_driver mdm_modem_driver = { struct modem_driver mdm_modem_driver = {
.name = "modemap driver", .name = "modemap driver",
.start = modemap_start, .start = modemap_start,
.stop = modemap_stop, .stop = modemap_stop,
.ioctl = modemap_ioctl, .ioctl = modemap_ioctl,
}; };
static int socket_start (struct modem *m) static int socket_start (struct modem *m)
@ -626,20 +626,27 @@ static int socket_start (struct modem *m)
exit(-1); exit(-1);
} }
pid_t ppid = getpid();
pid_t pid = fork(); pid_t pid = fork();
if (pid == -1) { if (pid == -1) {
perror("fork"); perror("fork");
exit(-1); exit(-1);
} }
if (pid == 0) { // child if (pid == 0) { // child
char str[16]; char _str[12];
snprintf(str,sizeof(str),"%d",sockets[0]); char _parent[12];
snprintf(_str,sizeof(_str),"%d",sockets[0]);
snprintf(_parent,sizeof(_parent),"%d",ppid);
const char *_modem_path = strrchr(modem_dev_name, '/');
_modem_path = _modem_path == NULL ? modem_dev_name : _modem_path + 1;
close(sockets[1]); close(sockets[1]);
if(m->hook == MODEM_HOOK_SNOOPING) { if(_MODEM_DO_ANSWER) {
ret = execl(modem_exec,modem_exec,"rr",str,NULL); DBG("MODEM_ANSW execl arg: %s, %s, %s, %s, %s\n",modem_exec,"rr",_str,_parent,_modem_path);
ret = execl(modem_exec,modem_exec,"rr",_str,_parent,_modem_path,NULL);
} }
else { else {
ret = execl(modem_exec,modem_exec,m->dial_string,str,NULL); DBG("MODEM_DIAL execl arg: %s, %s, %s, %s, %s\n",modem_exec,m->dial_string,_str,_parent,_modem_path);
ret = execl(modem_exec,modem_exec,m->dial_string,_str,_parent,_modem_path,NULL);
} }
if (ret == -1) { if (ret == -1) {
ERR("prog: %s\n", modem_exec); ERR("prog: %s\n", modem_exec);
@ -653,13 +660,13 @@ static int socket_start (struct modem *m)
ret = 192*2; ret = 192*2;
memset(outbuf, 0 , ret); memset(outbuf, 0 , ret);
ret = write(dev->fd, outbuf, ret); ret = write(dev->fd, outbuf, ret);
DBG("done delay thing\n");
if (ret < 0) { if (ret < 0) {
close(dev->fd); close(dev->fd);
dev->fd = -1; dev->fd = -1;
return ret; return ret;
} }
dev->delay = ret/2; dev->delay = ret/2;
DBG("done delay thing %d\n", dev->delay);
} }
return 0; return 0;
} }
@ -713,10 +720,10 @@ static int socket_ioctl(struct modem *m, unsigned int cmd, unsigned long arg)
} }
struct modem_driver socket_modem_driver = { struct modem_driver socket_modem_driver = {
.name = "socket driver", .name = "socket driver",
.start = socket_start, .start = socket_start,
.stop = socket_stop, .stop = socket_stop,
.ioctl = socket_ioctl, .ioctl = socket_ioctl,
}; };
static int mdm_device_read(struct device_struct *dev, char *buf, int size) static int mdm_device_read(struct device_struct *dev, char *buf, int size)
@ -788,11 +795,11 @@ int create_pty(struct modem *m)
if(m->pty) if(m->pty)
close(m->pty); close(m->pty);
pty = getpt(); pty = getpt();
if (pty < 0 || grantpt(pty) < 0 || unlockpt(pty) < 0) { if (pty < 0 || grantpt(pty) < 0 || unlockpt(pty) < 0) {
ERR("getpt: %s\n", strerror(errno)); ERR("getpt: %s\n", strerror(errno));
return -1; return -1;
} }
if(m->pty) { if(m->pty) {
termios = m->termios; termios = m->termios;
@ -805,11 +812,11 @@ int create_pty(struct modem *m)
cfsetospeed(&termios, B115200); cfsetospeed(&termios, B115200);
} }
ret = tcsetattr(pty, TCSANOW, &termios); ret = tcsetattr(pty, TCSANOW, &termios);
if (ret) { if (ret) {
ERR("tcsetattr: %s\n",strerror(errno)); ERR("tcsetattr: %s\n",strerror(errno));
return -1; return -1;
} }
fcntl(pty,F_SETFL,O_NONBLOCK); fcntl(pty,F_SETFL,O_NONBLOCK);
@ -820,17 +827,18 @@ int create_pty(struct modem *m)
modem_update_termios(m,&termios); modem_update_termios(m,&termios);
modem_group = NULL;
if(modem_group && *modem_group) { if(modem_group && *modem_group) {
struct group *grp = getgrnam(modem_group); struct group *grp = getgrnam(modem_group);
if(!grp) { if(!grp) {
ERR("cannot find group '%s': %s\n", modem_group, ERR("cannot find group '%s': %s\n", modem_group,
strerror(errno)); strerror(errno));
} }
else { else {
ret = chown(pty_name, -1, grp->gr_gid); ret = chown(pty_name, -1, grp->gr_gid);
if(ret < 0) { if(ret < 0) {
ERR("cannot chown '%s' to ':%s': %s\n", ERR("cannot chown '%s' to ':%s': %s\n",
pty_name, modem_group, strerror(errno)); pty_name, modem_group, strerror(errno));
} }
} }
} }
@ -838,19 +846,19 @@ int create_pty(struct modem *m)
ret = chmod(pty_name, modem_perm); ret = chmod(pty_name, modem_perm);
if (ret < 0) { if (ret < 0) {
ERR("cannot chmod '%s' to %o: %s\n", ERR("cannot chmod '%s' to %o: %s\n",
pty_name, modem_perm, strerror(errno)); pty_name, modem_perm, strerror(errno));
} }
if(*link_name) { if(*link_name) {
unlink(link_name); unlink(link_name);
if(symlink(pty_name,link_name)) { if(symlink(pty_name,link_name)) {
ERR("cannot create symbolink link `%s' -> `%s': %s\n", ERR("cannot create symbolink link `%s' -> `%s': %s\n",
link_name,pty_name,strerror(errno)); link_name,pty_name,strerror(errno));
*link_name = '\0'; *link_name = '\0';
} }
else { else {
INFO("symbolic link `%s' -> `%s' created.\n", INFO("symbolic link `%s' -> `%s' created.\n",
link_name, pty_name); link_name, pty_name);
} }
} }
@ -898,9 +906,9 @@ static int modem_run(struct modem *m, struct device_struct *dev)
modem_ring_detector_start(m); modem_ring_detector_start(m);
#endif #endif
tmo.tv_sec = 1; tmo.tv_sec = 1;
tmo.tv_usec= 0; tmo.tv_usec= 0;
FD_ZERO(&rset); FD_ZERO(&rset);
FD_ZERO(&eset); FD_ZERO(&eset);
if(m->started) if(m->started)
FD_SET(dev->fd,&rset); FD_SET(dev->fd,&rset);
@ -918,14 +926,14 @@ static int modem_run(struct modem *m, struct device_struct *dev)
if(m->pty > max_fd) max_fd = m->pty; if(m->pty > max_fd) max_fd = m->pty;
} }
ret = select(max_fd + 1,&rset,NULL,&eset,&tmo); ret = select(max_fd + 1,&rset,NULL,&eset,&tmo);
if (ret < 0) { if (ret < 0) {
if (errno == EINTR) if (errno == EINTR)
continue; continue;
ERR("select: %s\n",strerror(errno)); ERR("select: %s\n",strerror(errno));
return ret; return ret;
} }
if ( ret == 0 ) if ( ret == 0 )
continue; continue;
@ -1002,7 +1010,7 @@ static int modem_run(struct modem *m, struct device_struct *dev)
} }
if(count != m->update_delay) { if(count != m->update_delay) {
ERR("cannot update delay: %d instead of %d.\n", ERR("cannot update delay: %d instead of %d.\n",
count, m->update_delay); count, m->update_delay);
return -1; return -1;
} }
dev->delay += m->update_delay; dev->delay += m->update_delay;
@ -1074,7 +1082,7 @@ int modem_main(const char *dev_name)
struct modem *m; struct modem *m;
int pty; int pty;
int ret = 0; int ret = 0;
struct passwd *pwd; // struct passwd *pwd;
modem_debug_init(basename(dev_name)); modem_debug_init(basename(dev_name));
@ -1089,7 +1097,7 @@ int modem_main(const char *dev_name)
prop_dp_init(); prop_dp_init();
modem_timer_init(); modem_timer_init();
sprintf(link_name,"/dev/ttySL%d", device.num); sprintf(link_name, "%s", modem_dev_name);
m = modem_create(modem_driver,basename(dev_name)); m = modem_create(modem_driver,basename(dev_name));
m->name = basename(dev_name); m->name = basename(dev_name);
@ -1103,10 +1111,10 @@ int modem_main(const char *dev_name)
} }
INFO("modem `%s' created. TTY is `%s'\n", INFO("modem `%s' created. TTY is `%s'\n",
m->name, m->pty_name); m->name, m->pty_name);
sprintf(path_name,"/var/lib/slmodem/data.%s",basename(dev_name)); // sprintf(path_name,"/var/lib/slmodem/data.%s",basename(dev_name));
datafile_load_info(path_name,&m->dsp_info); // datafile_load_info(path_name,&m->dsp_info);
if (need_realtime) { if (need_realtime) {
struct sched_param prm; struct sched_param prm;
@ -1147,11 +1155,11 @@ int modem_main(const char *dev_name)
} }
ret = (setgroups(1,&pwd->pw_gid) || ret = (setgroups(1,&pwd->pw_gid) ||
setgid(pwd->pw_gid) || setgid(pwd->pw_gid) ||
setuid(pwd->pw_uid)); setuid(pwd->pw_uid));
if (ret) { if (ret) {
ERR("setgroups or setgid %ld or setuid %ld failed: %s\n", ERR("setgroups or setgid %ld or setuid %ld failed: %s\n",
(long)pwd->pw_gid,(long)pwd->pw_uid,strerror(errno)); (long)pwd->pw_gid,(long)pwd->pw_uid,strerror(errno));
exit(-1); exit(-1);
} }
@ -1160,11 +1168,11 @@ int modem_main(const char *dev_name)
exit(-1); exit(-1);
} }
DBG("dropped privileges to %ld.%ld\n", DBG("dropped privileges to %ld.%ld\n",
(long)pwd->pw_gid,(long)pwd->pw_uid); (long)pwd->pw_gid,(long)pwd->pw_uid);
#endif #endif
INFO("Use `%s' as modem device, Ctrl+C for termination.\n", INFO("Use `%s' as modem device, Ctrl+C for termination.\n",
*link_name ? link_name : m->pty_name); *link_name ? link_name : m->pty_name);
/* main loop here */ /* main loop here */
ret = modem_run(m,&device); ret = modem_run(m,&device);
@ -1199,7 +1207,7 @@ int main(int argc, char *argv[])
extern void modem_cmdline(int argc, char *argv[]); extern void modem_cmdline(int argc, char *argv[]);
int ret; int ret;
modem_cmdline(argc,argv); modem_cmdline(argc,argv);
if(!modem_dev_name) modem_dev_name = "/dev/slamr0"; if(!modem_dev_name) modem_dev_name = "/tmp/ttySL0";
device_setup = socket_device_setup; device_setup = socket_device_setup;
device_release = mdm_device_release; device_release = mdm_device_release;