├── README.md ├── echotest ├── echo.htm ├── echotest.cfg └── echotest.go ├── info └── info.go ├── janus ├── janus.go └── types.go └── list-sessions └── list-sessions.go /README.md: -------------------------------------------------------------------------------- 1 | Example usage of simple Unix Datagram transport for Janus Gateway. 2 | 3 | ## info 4 | Connects to gateway and issues the `info` Janus API request. 5 | 6 | ``` 7 | janus-info /path/to/unix/socket 8 | ``` 9 | 10 | ## echotest 11 | Starts an echotest server. See configuration options in echotest/echotest.cfg 12 | 13 | ``` 14 | echotest /path/to/echotest.cfg 15 | ``` 16 | 17 | ## list-sessions 18 | Connects to gateway and issues the `list_sessions` Admin API request. 19 | 20 | ``` 21 | janus-list-sessions /path/to/unix/socket [admin_secret] 22 | ``` 23 | -------------------------------------------------------------------------------- /echotest/echo.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 22 | 23 | 24 | 25 | 92 | 93 | -------------------------------------------------------------------------------- /echotest/echotest.cfg: -------------------------------------------------------------------------------- 1 | # Echotest Configuration 2 | 3 | # HTTP port to listen on 4 | port = 8080 5 | html = "/opt/janus/srv/http/echo.htm" 6 | 7 | # Path to Janus transport socket 8 | path = "/opt/janus/var/run/janus" 9 | 10 | # Configure multiple STUN/TURN servers by adding extra entries 11 | [[iceServer]] 12 | urls = "stun:stun.l.google.com:19302" 13 | 14 | # Configure an authenticated STUN/TURN server 15 | #[[iceServer]] 16 | #urls = "stun:stun.server.com" 17 | #username = "user@server.com" 18 | #credential = "password" 19 | -------------------------------------------------------------------------------- /echotest/echotest.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/burntsushi/toml" 7 | "github.com/nowylie/go-janus/janus" 8 | "html/template" 9 | "net/http" 10 | "os" 11 | "time" 12 | ) 13 | 14 | var gateway *janus.Gateway 15 | 16 | type Config struct { 17 | Port uint 18 | Html string 19 | Path string 20 | Servers []Server `toml:"iceServer"` 21 | } 22 | 23 | type Server struct { 24 | Urls string `json:"urls"` 25 | Username string `json:"username,omitempty"` 26 | Credential string `json:"password,omitempty"` 27 | } 28 | 29 | var config Config 30 | var output *template.Template 31 | 32 | func main() { 33 | var err error 34 | 35 | if len(os.Args) < 2 { 36 | fmt.Printf("usage: janus-echotest \n") 37 | return 38 | } 39 | 40 | if _, err := toml.DecodeFile(os.Args[1], &config); err != nil { 41 | fmt.Printf("toml.Decode: %s\n", err) 42 | return 43 | } 44 | 45 | output, err = template.ParseFiles(config.Html) 46 | if err != nil { 47 | fmt.Printf("template.ParseFiles: %s\n", err) 48 | return 49 | } 50 | 51 | gateway, err = janus.Connect(config.Path) 52 | if err != nil { 53 | fmt.Printf("Connect: %s\n") 54 | return 55 | } 56 | 57 | http.HandleFunc("/", EchoTest) 58 | http.ListenAndServe(fmt.Sprintf(":%d", config.Port), nil) 59 | } 60 | 61 | type Request struct { 62 | Body interface{} 63 | Offer interface{} 64 | Candidates []interface{} 65 | } 66 | 67 | func EchoTest(w http.ResponseWriter, r *http.Request) { 68 | fmt.Printf("%s %s %s\n", r.Method, r.URL, r.Proto) 69 | if r.Method == "GET" { 70 | servers, err := json.Marshal(config.Servers) 71 | if err != nil { 72 | fmt.Printf("json.Marshal: %s\n", err) 73 | return 74 | } 75 | 76 | err = output.Execute(w, template.JS(servers)) 77 | if err != nil { 78 | fmt.Printf("template.Execute: %s\n", err) 79 | return 80 | } 81 | return 82 | } 83 | 84 | session, err := gateway.Create() 85 | if err != nil { 86 | fmt.Printf("gateway.Create: %s\n", err) 87 | return 88 | } 89 | 90 | stop := make(chan struct{}) 91 | go keepalive(session, stop) 92 | 93 | handle, err := session.Attach("janus.plugin.echotest") 94 | if err != nil { 95 | fmt.Printf("session.Attach: %s\n", err) 96 | return 97 | } 98 | go watch(session, handle, stop) 99 | 100 | decoder := json.NewDecoder(r.Body) 101 | var request Request 102 | err = decoder.Decode(&request) 103 | if err != nil { 104 | fmt.Printf("json.Unmarshal: %s\n", err) 105 | return 106 | } 107 | 108 | event, err := handle.Message(request.Body, nil) 109 | if err != nil { 110 | fmt.Printf("handle.Message: %s\n", err) 111 | return 112 | } 113 | 114 | event, err = handle.Message(request.Body, request.Offer) 115 | if err != nil { 116 | fmt.Printf("handle.Message: %s\n", err) 117 | return 118 | } 119 | 120 | out, err := json.Marshal(event.Jsep) 121 | if err != nil { 122 | fmt.Printf("json.Marshal: %s\n", err) 123 | return 124 | } 125 | w.Write(out) 126 | 127 | _, err = handle.TrickleMany(request.Candidates) 128 | if err != nil { 129 | fmt.Printf("handle.Trickle: %s\n", err) 130 | return 131 | } 132 | } 133 | 134 | func watch(session *janus.Session, handle *janus.Handle, stop chan struct{}) { 135 | for { 136 | msg := <-handle.Events 137 | switch msg := msg.(type) { 138 | case *janus.MediaMsg: 139 | if msg.Receiving == "false" { 140 | handle.Detach() 141 | session.Destroy() 142 | close(stop) 143 | return 144 | } 145 | } 146 | } 147 | } 148 | 149 | func keepalive(session *janus.Session, stop chan struct{}) { 150 | ticker := time.NewTicker(time.Second * 30) 151 | 152 | for { 153 | select { 154 | case <-ticker.C: 155 | session.KeepAlive() 156 | case <-stop: 157 | ticker.Stop() 158 | return 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /info/info.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/nowylie/go-janus/janus" 6 | "os" 7 | "encoding/json" 8 | ) 9 | 10 | func main() { 11 | if len(os.Args) < 2 { 12 | fmt.Printf("usage: janus-echotest \n") 13 | return 14 | } 15 | 16 | gateway, err := janus.Connect(os.Args[1]) 17 | if err != nil { 18 | fmt.Printf("Connect: %s\n") 19 | return 20 | } 21 | 22 | info, err := gateway.Info() 23 | if err != nil { 24 | fmt.Printf("Info: %s\n", err) 25 | return 26 | } 27 | 28 | infoStr, _ := json.MarshalIndent(info, "", "\t") 29 | fmt.Printf("%s\n", string(infoStr)) 30 | gateway.Close() 31 | } 32 | -------------------------------------------------------------------------------- /janus/janus.go: -------------------------------------------------------------------------------- 1 | // Package janus is a Golang implementation of the Janus API, used to interact 2 | // with the Janus WebRTC Gateway. 3 | package janus 4 | 5 | import ( 6 | "bytes" 7 | "encoding/json" 8 | "fmt" 9 | "net" 10 | "os" 11 | "strconv" 12 | "sync" 13 | "sync/atomic" 14 | ) 15 | 16 | var debug = false 17 | 18 | func init() { 19 | if os.Getenv("DEBUG") != "" { 20 | debug = true 21 | } 22 | } 23 | 24 | func unexpected(request string) error { 25 | return fmt.Errorf("Unexpected response received to '%s' request", request) 26 | } 27 | 28 | func newRequest(method string) (map[string]interface{}, chan interface{}) { 29 | req := make(map[string]interface{}, 8) 30 | req["janus"] = method 31 | return req, make(chan interface{}) 32 | } 33 | 34 | // Gateway represents a connection to an instance of the Janus Gateway. 35 | type Gateway struct { 36 | // Sessions is a map of the currently active sessions to the gateway. 37 | Sessions map[uint64]*Session 38 | 39 | // Access to the Sessions map should be synchronized with the Gateway.Lock() 40 | // and Gateway.Unlock() methods provided by the embeded sync.Mutex. 41 | sync.Mutex 42 | 43 | conn net.Conn 44 | nextTransaction uint64 45 | transactions map[uint64]chan interface{} 46 | } 47 | 48 | // Connect creates a new Gateway instance, connected to the Janus Gateway. 49 | // path should be a filesystem path to the Unix Socket that the Unix transport 50 | // is bound to. 51 | // On success, a new Gateway object will be returned and error will be nil. 52 | func Connect(path string) (*Gateway, error) { 53 | lpath := fmt.Sprintf("/tmp/janus-echotest.%d", os.Getpid()) 54 | laddr := &net.UnixAddr{lpath, "unixgram"} 55 | raddr := &net.UnixAddr{rpath, "unixgram"} 56 | conn, err := net.DialUnix("unixgram", laddr, raddr) 57 | if err != nil { 58 | return nil, err 59 | } 60 | 61 | gateway := new(Gateway) 62 | gateway.conn = conn 63 | gateway.transactions = make(map[uint64]chan interface{}) 64 | gateway.Sessions = make(map[uint64]*Session) 65 | 66 | go gateway.recv() 67 | return gateway, nil 68 | } 69 | 70 | // Close closes the underlying connection to the Gateway. 71 | func (gateway *Gateway) Close() error { 72 | return gateway.conn.Close() 73 | } 74 | 75 | func (gateway *Gateway) send(msg map[string]interface{}, transaction chan interface{}) { 76 | id := atomic.AddUint64(&gateway.nextTransaction, 1) 77 | 78 | msg["transaction"] = strconv.FormatUint(id, 10) 79 | gateway.Lock() 80 | gateway.transactions[id] = transaction 81 | gateway.Unlock() 82 | 83 | data, err := json.Marshal(msg) 84 | if err != nil { 85 | fmt.Printf("json.Marshal: %s\n", err) 86 | return 87 | } 88 | 89 | if debug { 90 | // log message being sent 91 | var log bytes.Buffer 92 | json.Indent(&log, data, ">", " ") 93 | log.Write([]byte("\n")) 94 | log.WriteTo(os.Stdout) 95 | } 96 | 97 | _, err = gateway.conn.Write(data) 98 | if err != nil { 99 | fmt.Printf("conn.Write: %s\n", err) 100 | return 101 | } 102 | } 103 | 104 | func passMsg(ch chan interface{}, msg interface{}) { 105 | ch <- msg 106 | } 107 | 108 | func (gateway *Gateway) recv() { 109 | var log bytes.Buffer 110 | buffer := make([]byte, 8192) 111 | 112 | for { 113 | // Read message from Gateway 114 | n, err := gateway.conn.Read(buffer) 115 | if err != nil { 116 | fmt.Printf("conn.Read: %s\n", err) 117 | return 118 | } 119 | 120 | if debug { 121 | // Log received message 122 | json.Indent(&log, buffer[:n], "<", " ") 123 | log.Write([]byte("\n")) 124 | log.WriteTo(os.Stdout) 125 | } 126 | 127 | // Decode to Msg struct 128 | var base BaseMsg 129 | if err := json.Unmarshal(buffer[:n], &base); err != nil { 130 | fmt.Printf("json.Unmarshal: %s\n", err) 131 | continue 132 | } 133 | 134 | typeFunc, ok := msgtypes[base.Type] 135 | if !ok { 136 | fmt.Printf("Unknown message type received!\n") 137 | continue 138 | } 139 | 140 | msg := typeFunc() 141 | if err := json.Unmarshal(buffer[:n], &msg); err != nil { 142 | fmt.Printf("json.Unmarshal: %s\n", err) 143 | continue // Decode error 144 | } 145 | 146 | // Pass message on from here 147 | if base.Id == "" { 148 | // Is this a Handle event? 149 | if base.Handle == 0 { 150 | // Nope. No idea what's going on... 151 | // Error() 152 | } else { 153 | // Lookup Session 154 | gateway.Lock() 155 | session := gateway.Sessions[base.Session] 156 | gateway.Unlock() 157 | if session == nil { 158 | fmt.Printf("Unable to deliver message. Session gone?\n") 159 | continue 160 | } 161 | 162 | // Lookup Handle 163 | session.Lock() 164 | handle := session.Handles[base.Handle] 165 | session.Unlock() 166 | if handle == nil { 167 | fmt.Printf("Unable to deliver message. Handle gone?\n") 168 | continue 169 | } 170 | 171 | // Pass msg 172 | go passMsg(handle.Events, msg) 173 | } 174 | } else { 175 | id, _ := strconv.ParseUint(base.Id, 10, 64) // FIXME: error checking 176 | // Lookup Transaction 177 | gateway.Lock() 178 | transaction := gateway.transactions[id] 179 | gateway.Unlock() 180 | if transaction == nil { 181 | // Error() 182 | } 183 | 184 | // Pass msg 185 | go passMsg(transaction, msg) 186 | } 187 | } 188 | } 189 | 190 | // Info sends an info request to the Gateway. 191 | // On success, an InfoMsg will be returned and error will be nil. 192 | func (gateway *Gateway) Info() (*InfoMsg, error) { 193 | req, ch := newRequest("info") 194 | gateway.send(req, ch) 195 | 196 | msg := <-ch 197 | switch msg := msg.(type) { 198 | case *InfoMsg: 199 | return msg, nil 200 | case *ErrorMsg: 201 | return nil, msg 202 | } 203 | 204 | return nil, unexpected("info") 205 | } 206 | 207 | // Create sends a create request to the Gateway. 208 | // On success, a new Session will be returned and error will be nil. 209 | func (gateway *Gateway) Create() (*Session, error) { 210 | req, ch := newRequest("create") 211 | gateway.send(req, ch) 212 | 213 | msg := <-ch 214 | var success *SuccessMsg 215 | switch msg := msg.(type) { 216 | case *SuccessMsg: 217 | success = msg 218 | case *ErrorMsg: 219 | return nil, msg 220 | } 221 | 222 | // Create new session 223 | session := new(Session) 224 | session.gateway = gateway 225 | session.Id = success.Data.Id 226 | session.Handles = make(map[uint64]*Handle) 227 | 228 | // Store this session 229 | gateway.Lock() 230 | gateway.Sessions[session.Id] = session 231 | gateway.Unlock() 232 | 233 | return session, nil 234 | } 235 | 236 | // Session represents a session instance on the Janus Gateway. 237 | type Session struct { 238 | // Id is the session_id of this session 239 | Id uint64 240 | 241 | // Handles is a map of plugin handles within this session 242 | Handles map[uint64]*Handle 243 | 244 | // Access to the Handles map should be synchronized with the Session.Lock() 245 | // and Session.Unlock() methods provided by the embeded sync.Mutex. 246 | sync.Mutex 247 | 248 | gateway *Gateway 249 | } 250 | 251 | func (session *Session) send(msg map[string]interface{}, transaction chan interface{}) { 252 | msg["session_id"] = session.Id 253 | session.gateway.send(msg, transaction) 254 | } 255 | 256 | // Attach sends an attach request to the Gateway within this session. 257 | // plugin should be the unique string of the plugin to attach to. 258 | // On success, a new Handle will be returned and error will be nil. 259 | func (session *Session) Attach(plugin string) (*Handle, error) { 260 | req, ch := newRequest("attach") 261 | req["plugin"] = plugin 262 | session.send(req, ch) 263 | 264 | var success *SuccessMsg 265 | msg := <-ch 266 | switch msg := msg.(type) { 267 | case *SuccessMsg: 268 | success = msg 269 | case *ErrorMsg: 270 | return nil, msg 271 | } 272 | 273 | handle := new(Handle) 274 | handle.session = session 275 | handle.Id = success.Data.Id 276 | handle.Events = make(chan interface{}, 8) 277 | 278 | session.Lock() 279 | session.Handles[handle.Id] = handle 280 | session.Unlock() 281 | 282 | return handle, nil 283 | } 284 | 285 | // KeepAlive sends a keep-alive request to the Gateway. 286 | // On success, an AckMsg will be returned and error will be nil. 287 | func (session *Session) KeepAlive() (*AckMsg, error) { 288 | req, ch := newRequest("keepalive") 289 | session.send(req, ch) 290 | 291 | msg := <-ch 292 | switch msg := msg.(type) { 293 | case *AckMsg: 294 | return msg, nil 295 | case *ErrorMsg: 296 | return nil, msg 297 | } 298 | 299 | return nil, unexpected("keepalive") 300 | } 301 | 302 | // Destroy sends a destroy request to the Gateway to tear down this session. 303 | // On success, the Session will be removed from the Gateway.Sessions map, an 304 | // AckMsg will be returned and error will be nil. 305 | func (session *Session) Destroy() (*AckMsg, error) { 306 | req, ch := newRequest("destroy") 307 | session.send(req, ch) 308 | 309 | var ack *AckMsg 310 | msg := <-ch 311 | switch msg := msg.(type) { 312 | case *AckMsg: 313 | ack = msg 314 | case *ErrorMsg: 315 | return nil, msg 316 | } 317 | 318 | // Remove this session from the gateway 319 | session.gateway.Lock() 320 | delete(session.gateway.Sessions, session.Id) 321 | session.gateway.Unlock() 322 | 323 | return ack, nil 324 | } 325 | 326 | // Handle represents a handle to a plugin instance on the Gateway. 327 | type Handle struct { 328 | // Id is the handle_id of this plugin handle 329 | Id uint64 330 | 331 | // Events is a receive only channel that can be used to receive events 332 | // related to this handle from the gateway. 333 | Events <-chan interface{} 334 | 335 | session *Session 336 | } 337 | 338 | func (handle *Handle) send(msg map[string]interface{}, transaction chan interface{}) { 339 | msg["handle_id"] = handle.Id 340 | handle.session.send(msg, transaction) 341 | } 342 | 343 | // Message sends a message request to a plugin handle on the Gateway. 344 | // body should be the plugin data to be passed to the plugin, and jsep should 345 | // contain an optional SDP offer/answer to establish a WebRTC PeerConnection. 346 | // On success, an EventMsg will be returned and error will be nil. 347 | func (handle *Handle) Message(body, jsep interface{}) (*EventMsg, error) { 348 | req, ch := newRequest("message") 349 | if body != nil { 350 | req["body"] = body 351 | } 352 | if jsep != nil { 353 | req["jsep"] = jsep 354 | } 355 | handle.send(req, ch) 356 | 357 | GetMessage: // No tears.. 358 | msg := <-ch 359 | switch msg := msg.(type) { 360 | case *AckMsg: 361 | goto GetMessage // ..only dreams. 362 | case *EventMsg: 363 | return msg, nil 364 | case *ErrorMsg: 365 | return nil, msg 366 | } 367 | 368 | return nil, unexpected("message") 369 | } 370 | 371 | // Trickle sends a trickle request to the Gateway as part of establishing 372 | // a new PeerConnection with a plugin. 373 | // candidate should be a single ICE candidate, or a completed object to 374 | // signify that all candidates have been sent: 375 | // { 376 | // "completed": true 377 | // } 378 | // On success, an AckMsg will be returned and error will be nil. 379 | func (handle *Handle) Trickle(candidate interface{}) (*AckMsg, error) { 380 | req, ch := newRequest("trickle") 381 | req["candidate"] = candidate 382 | handle.send(req, ch) 383 | 384 | msg := <-ch 385 | switch msg := msg.(type) { 386 | case *AckMsg: 387 | return msg, nil 388 | case *ErrorMsg: 389 | return nil, msg 390 | } 391 | 392 | return nil, unexpected("trickle") 393 | } 394 | 395 | // TrickleMany sends a trickle request to the Gateway as part of establishing 396 | // a new PeerConnection with a plugin. 397 | // candidates should be an array of ICE candidates. 398 | // On success, an AckMsg will be returned and error will be nil. 399 | func (handle *Handle) TrickleMany(candidates interface{}) (*AckMsg, error) { 400 | req, ch := newRequest("trickle") 401 | req["candidates"] = candidates 402 | handle.send(req, ch) 403 | 404 | msg := <-ch 405 | switch msg := msg.(type) { 406 | case *AckMsg: 407 | return msg, nil 408 | case *ErrorMsg: 409 | return nil, msg 410 | } 411 | 412 | return nil, unexpected("trickle") 413 | } 414 | 415 | // Detach sends a detach request to the Gateway to remove this handle. 416 | // On success, an AckMsg will be returned and error will be nil. 417 | func (handle *Handle) Detach() (*AckMsg, error) { 418 | req, ch := newRequest("detach") 419 | handle.send(req, ch) 420 | 421 | var ack *AckMsg 422 | msg := <-ch 423 | switch msg := msg.(type) { 424 | case *AckMsg: 425 | ack = msg 426 | case *ErrorMsg: 427 | return nil, msg 428 | } 429 | 430 | // Remove this handle from the session 431 | handle.session.Lock() 432 | delete(handle.session.Handles, handle.Id) 433 | handle.session.Unlock() 434 | 435 | return ack, nil 436 | } 437 | -------------------------------------------------------------------------------- /janus/types.go: -------------------------------------------------------------------------------- 1 | // Msg Types 2 | // 3 | // All messages received from the gateway are first decoded to the BaseMsg 4 | // type. The BaseMsg type extracts the following JSON from the message: 5 | // { 6 | // "janus": , 7 | // "transaction": , 8 | // "session_id": , 9 | // "sender": 10 | // } 11 | // The Type field is inspected to determine which concrete type 12 | // to decode the message to, while the other fields (Id/Session/Handle) are 13 | // inspected to determine where the message should be delivered. Messages 14 | // with an Id field defined are considered responses to previous requests, and 15 | // will be passed directly to requester. Messages without an Id field are 16 | // considered unsolicited events from the gateway and are expected to have 17 | // both Session and Handle fields defined. They will be passed to the Events 18 | // channel of the related Handle and can be read from there. 19 | package janus 20 | 21 | var msgtypes = map[string]func() interface{}{ 22 | "error": func() interface{} { return &ErrorMsg{} }, 23 | "success": func() interface{} { return &SuccessMsg{} }, 24 | "detached": func() interface{} { return &DetachedMsg{} }, 25 | "server_info": func() interface{} { return &InfoMsg{} }, 26 | "ack": func() interface{} { return &AckMsg{} }, 27 | "event": func() interface{} { return &EventMsg{} }, 28 | "webrtcup": func() interface{} { return &WebRTCUpMsg{} }, 29 | "media": func() interface{} { return &MediaMsg{} }, 30 | "hangup": func() interface{} { return &HangupMsg{} }, 31 | } 32 | 33 | type BaseMsg struct { 34 | Type string `json:"janus"` 35 | Id string `json:"transaction"` 36 | Session uint64 `json:"session_id"` 37 | Handle uint64 `json:"sender"` 38 | } 39 | 40 | type ErrorMsg struct { 41 | Err ErrorData `json:"error"` 42 | } 43 | 44 | type ErrorData struct { 45 | Code int 46 | Reason string 47 | } 48 | 49 | func (err *ErrorMsg) Error() string { 50 | return err.Err.Reason 51 | } 52 | 53 | type SuccessMsg struct { 54 | Data SuccessData 55 | } 56 | 57 | type SuccessData struct { 58 | Id uint64 59 | } 60 | 61 | type DetachedMsg struct{} 62 | 63 | type InfoMsg struct { 64 | Name string 65 | Version int 66 | VersionString string `json:"version_string"` 67 | Author string 68 | DataChannels string `json:"data_channels"` 69 | IPv6 string `json:"ipv6"` 70 | ICE_TCP string `json:"ice-tcp"` 71 | Transports map[string]PluginInfo 72 | Plugins map[string]PluginInfo 73 | } 74 | 75 | type PluginInfo struct { 76 | Name string 77 | Author string 78 | Description string 79 | Version int 80 | VersionString string `json:"version_string"` 81 | } 82 | 83 | type AckMsg struct{} 84 | 85 | type EventMsg struct { 86 | Plugindata PluginData 87 | Jsep map[string]interface{} 88 | } 89 | 90 | type PluginData struct { 91 | Plugin string 92 | Data map[string]interface{} 93 | } 94 | 95 | type WebRTCUpMsg struct{} 96 | 97 | type MediaMsg struct { 98 | Type string 99 | Receiving string 100 | } 101 | 102 | type HangupMsg struct { 103 | Reason string 104 | } 105 | -------------------------------------------------------------------------------- /list-sessions/list-sessions.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "net" 8 | "os" 9 | ) 10 | 11 | func main() { 12 | upath := fmt.Sprintf("/tmp/janus-info.%d", os.Getpid()) 13 | 14 | // Address of local socket 15 | laddr := &net.UnixAddr{upath, "unixgram"} 16 | 17 | // Address of admin socket 18 | raddr := &net.UnixAddr{os.Args[1], "unixgram"} 19 | 20 | // Create unix datagram socket 21 | conn, _ := net.DialUnix("unixgram", laddr, raddr) 22 | 23 | // Create 'list_sessions' request 24 | list_sessions := make(map[string]string) 25 | list_sessions["janus"] = "list_sessions" 26 | list_sessions["transaction"] = "1234" 27 | list_sessions["admin_secret"] = "" 28 | if len(os.Args) > 2 { 29 | list_sessions["admin_secret"] = os.Args[2] 30 | } 31 | 32 | // Marshal request to json and sent to Janus 33 | req, _ := json.Marshal(list_sessions) 34 | conn.Write(req) 35 | 36 | // Receive response 37 | res := make([]byte, 8192) 38 | n, _ := conn.Read(res) 39 | 40 | // Format output 41 | var out bytes.Buffer 42 | json.Indent(&out, res[:n], "", "\t") 43 | out.Write([]byte("\n")) 44 | 45 | // Write to stdout 46 | out.WriteTo(os.Stdout) 47 | 48 | // Cleanup local socket 49 | conn.Close() 50 | os.Remove(upath) 51 | } 52 | --------------------------------------------------------------------------------