CPPMyth
Library to interoperate with MythTV server
mythprotoevent.cpp
1 /*
2  * Copyright (C) 2014 Jean-Luc Barriere
3  *
4  * This Program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2, or (at your option)
7  * any later version.
8  *
9  * This Program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; see the file COPYING. If not, write to
16  * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
17  * MA 02110-1301 USA
18  * http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21 
22 #include "mythprotoevent.h"
23 #include "../private/debug.h"
24 #include "../private/socket.h"
25 #include "../private/os/threads/mutex.h"
26 #include "../private/builtin.h"
27 
28 #include <limits>
29 #include <cstdio>
30 #include <vector>
31 
32 using namespace Myth;
33 
34 void __tokenize(const std::string& str, const char *delimiters, std::vector<std::string>& tokens, bool trimnull = false)
35 {
36  std::string::size_type pa = 0, pb = 0;
37  unsigned n = 0;
38  // Counter n will break infinite loop. Max count is 255 tokens
39  while ((pb = str.find_first_of(delimiters, pb)) != std::string::npos && ++n < 255)
40  {
41  tokens.push_back(str.substr(pa, pb - pa));
42  do
43  {
44  pa = ++pb;
45  }
46  while (trimnull && str.find_first_of(delimiters, pb) == pb);
47  }
48  tokens.push_back(str.substr(pa));
49 }
50 
55 
56 ProtoEvent::ProtoEvent(const std::string& server, unsigned port)
57 : ProtoBase(server, port)
58 {
59 }
60 
61 bool ProtoEvent::Open()
62 {
63  bool ok = false;
64 
65  if (!OpenConnection(PROTO_EVENT_RCVBUF))
66  return false;
67 
68  if (m_protoVersion >= 75)
69  ok = Announce75();
70 
71  if (ok)
72  return true;
73  Close();
74  return false;
75 }
76 
77 void ProtoEvent::Close()
78 {
79  ProtoBase::Close();
80  // Clean hanging and disable retry
81  m_tainted = m_hang = false;
82 }
83 
84 bool ProtoEvent::Announce75()
85 {
86  OS::CLockGuard lock(*m_mutex);
87 
88  std::string cmd("ANN Monitor ");
89  cmd.append(m_socket->GetMyHostName()).append(" 1");
90  if (!SendCommand(cmd.c_str()))
91  return false;
92 
93  std::string field;
94  if (!ReadField(field) || !IsMessageOK(field))
95  goto out;
96  return true;
97 
98 out:
99  FlushMessage();
100  return false;
101 }
102 
103 SignalStatusPtr ProtoEvent::RcvSignalStatus()
104 {
105  SignalStatusPtr signal(new SignalStatus()); // Using default constructor
106  std::string field;
107  while (ReadField(field))
108  {
109  std::vector<std::string> tokens;
110  // Tokenize the content
111  __tokenize(field, " ", tokens, false);
112  // Fill my signal
113  if (tokens.size() > 1)
114  {
115  int64_t tmpi;
116  if (tokens[0] == "slock")
117  signal->lock = (tokens[1] == "1" ? true : false);
118  else if (tokens[0] == "signal")
119  signal->signal = (0 == string_to_int64(tokens[1].c_str(), &tmpi) ? (int)tmpi : 0);
120  else if (tokens[0] == "snr")
121  signal->snr = (0 == string_to_int64(tokens[1].c_str(), &tmpi) ? (int)tmpi : 0);
122  else if (tokens[0] == "ber")
123  signal->ber = (0 == string_to_int64(tokens[1].c_str(), &tmpi) ? (long)tmpi : 0);
124  else if (tokens[0] == "ucb")
125  signal->ucb = (0 == string_to_int64(tokens[1].c_str(), &tmpi) ? (long)tmpi : 0);
126  }
127  }
128  return signal;
129 }
130 
131 int ProtoEvent::RcvBackendMessage(unsigned timeout, EventMessage **msg)
132 {
133  OS::CLockGuard lock(*m_mutex);
134  struct timeval tv;
135  tv.tv_sec = timeout;
136  tv.tv_usec = 0;
137  int r = m_socket->Listen(&tv);
138  if (r > 0)
139  {
140  std::string field;
141  EventMessage *pmsg = new EventMessage();
142  pmsg->event = EVENT_UNKNOWN;
143  pmsg->subject.clear();
144  pmsg->program.reset();
145  pmsg->signal.reset();
146  if (RcvMessageLength() && ReadField(field) && field == "BACKEND_MESSAGE")
147  {
148  unsigned n = 0;
149  ReadField(field);
150  // Tokenize the subject
151  __tokenize(field, " ", pmsg->subject, false);
152  n = (unsigned)pmsg->subject.size();
153  DBG(DBG_DEBUG, "%s: %s (%u)\n", __FUNCTION__, field.c_str(), n);
154 
155  if (pmsg->subject[0] == "UPDATE_FILE_SIZE")
156  pmsg->event = EVENT_UPDATE_FILE_SIZE;
157  else if (pmsg->subject[0] == "DONE_RECORDING")
158  pmsg->event = EVENT_DONE_RECORDING;
159  else if (pmsg->subject[0] == "QUIT_LIVETV")
160  pmsg->event = EVENT_QUIT_LIVETV;
161  else if (pmsg->subject[0] == "LIVETV_WATCH")
162  pmsg->event = EVENT_LIVETV_WATCH;
163  else if (pmsg->subject[0] == "LIVETV_CHAIN")
164  pmsg->event = EVENT_LIVETV_CHAIN;
165  else if (pmsg->subject[0] == "SIGNAL")
166  {
167  pmsg->event = EVENT_SIGNAL;
168  pmsg->signal = RcvSignalStatus();
169  }
170  else if (pmsg->subject[0] == "RECORDING_LIST_CHANGE")
171  {
172  pmsg->event = EVENT_RECORDING_LIST_CHANGE;
173  if (n > 1 && pmsg->subject[1] == "UPDATE")
174  pmsg->program = RcvProgramInfo();
175  }
176  else if (pmsg->subject[0] == "SCHEDULE_CHANGE")
177  pmsg->event = EVENT_SCHEDULE_CHANGE;
178  else if (pmsg->subject[0] == "ASK_RECORDING")
179  {
180  pmsg->event = EVENT_ASK_RECORDING;
181  pmsg->program = RcvProgramInfo();
182  }
183  else if (pmsg->subject[0] == "CLEAR_SETTINGS_CACHE")
184  pmsg->event = EVENT_CLEAR_SETTINGS_CACHE;
185  else if (pmsg->subject[0] == "GENERATED_PIXMAP")
186  pmsg->event = EVENT_GENERATED_PIXMAP;
187  else if (pmsg->subject[0] == "SYSTEM_EVENT")
188  pmsg->event = EVENT_SYSTEM_EVENT;
189  else
190  pmsg->event = EVENT_UNKNOWN;
191  }
192 
193  FlushMessage();
194  *msg = pmsg;
195  return (m_hang ? -(ENOTCONN) : 1);
196  }
197  else if (r < 0)
198  return r;
199 
200  return ((ProtoBase::IsOpen() && !m_hang) ? 0 : -(ENOTCONN));
201 }
bool m_hang
Connection hang: while true allow retry.
Definition: mythprotobase.h:76
bool ReadField(std::string &field)
This is the main namespace that encloses all public classes.
Definition: mythcontrol.h:29
bool m_tainted
Connection has hung since last reset.
Definition: mythprotobase.h:77
int RcvBackendMessage(unsigned timeout, EventMessage **msg)
Wait for new backend message from event connection.