]> git.ipfire.org Git - thirdparty/shairport-sync.git/commitdiff
Change principal_conn_lock from a regular mutex to a read-write mutex, so it can...
authorMike Brady <4265913+mikebrady@users.noreply.github.com>
Thu, 21 Sep 2023 15:04:23 +0000 (16:04 +0100)
committerMike Brady <4265913+mikebrady@users.noreply.github.com>
Thu, 21 Sep 2023 15:04:23 +0000 (16:04 +0100)
Only allow access to the config.airplay_statusflags, build_bonjour_strings(NULL), mdns_update(NULL, secondary_txt_records) when read_lock is acquired on the principal_conn_lock.
Only allow changes to config.airplay_statusflags and only allow access to mdns_update if you are the principal conn.

The bonjour status flags and the info response plist calculations are still a dirty rotten hack.

rtsp.c

diff --git a/rtsp.c b/rtsp.c
index 56e06c934a2c45c098823c239adadf19f45a3499..5c750a2ab31a6159c707e5d72ea198972f370cbd 100644 (file)
--- a/rtsp.c
+++ b/rtsp.c
@@ -141,12 +141,11 @@ rtsp_conn_info **conns;
 
 int metadata_running = 0;
 
-// always lock this when trying to make a conn the principal conn,
-// e.g. during an ANNOUNCE (Classic AirPlay) or SETUP (AirPlay 2)
-pthread_mutex_t principal_conn_acquisition_lock = PTHREAD_MUTEX_INITIALIZER;
 
 // always lock this when accessing the principal conn value
-pthread_mutex_t principal_conn_lock = PTHREAD_MUTEX_INITIALIZER;
+// use a read lock when consulting and holding it
+// use a write lock if you want to change it
+pthread_rwlock_t principal_conn_lock = PTHREAD_RWLOCK_INITIALIZER;
 
 // always lock this when accessing the list of connection threads
 pthread_mutex_t conns_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -565,23 +564,24 @@ void cancel_all_RTSP_threads(airplay_stream_c stream_category, int except_this_o
 // other devices.
 
 void release_play_lock(rtsp_conn_info *conn) {
-  pthread_cleanup_debug_mutex_lock(&principal_conn_lock, 100000,
-                                   1); // don't let the principal_conn be changed
+  // no need thread cancellation points in here
+  pthread_rwlock_wrlock(&principal_conn_lock);
   if (principal_conn == conn) {        // if we have the player
     if (conn != NULL)
       debug(2, "Connection %d: principal_conn released.", conn->connection_number);
     principal_conn = NULL; // let it go
   }
-  pthread_cleanup_pop(1); // release the principal_conn lock
+  pthread_rwlock_unlock(&principal_conn_lock);
 }
 
 // stop the current principal_conn from playing if necessary and make conn the principal_conn.
 
 int get_play_lock(rtsp_conn_info *conn, int allow_session_interruption) {
   int response = 0;
-  pthread_cleanup_debug_mutex_lock(&principal_conn_lock, 100000, 1);
+  pthread_rwlock_wrlock(&principal_conn_lock);
+  pthread_cleanup_push(rwlock_unlock,(void *)&principal_conn_lock);
   if (principal_conn != NULL)
-    debug(2, "Connection %d: is requested to relinquish principal_conn.",
+    debug(1, "Connection %d: is requested to relinquish principal_conn.",
           principal_conn->connection_number);
   if (conn != NULL)
     debug(2, "Connection %d: request to acquire principal_conn.", conn->connection_number);
@@ -594,10 +594,10 @@ int get_play_lock(rtsp_conn_info *conn, int allow_session_interruption) {
       warn("Connection %d: request to re-acquire principal_conn!",
            principal_conn->connection_number);
   } else if (allow_session_interruption != 0) {
-    player_stop(principal_conn);
-    debug(2, "Connection %d: termination requested.", principal_conn->connection_number);
-    pthread_cancel(principal_conn->thread);
-    usleep(2000000);       // don't know why this delay is needed.
+    rtsp_conn_info *previous_principal_conn = principal_conn;
+    principal_conn = NULL; // no longer the principal conn
+    pthread_cancel(previous_principal_conn->thread);
+    // usleep(1000000);       // don't know why this delay is needed.
     principal_conn = conn; // make the conn the new principal_conn
     response = 1;          // interrupted an existing session
   } else {
@@ -766,6 +766,11 @@ void msg_retain(rtsp_message *msg) {
 }
 
 rtsp_message *msg_init(void) {
+  // no thread cancellation points here
+  int rc = pthread_mutex_lock(&reference_counter_lock);
+  if (rc)
+    debug(1, "Error %d locking reference counter lock");
+
   rtsp_message *msg = malloc(sizeof(rtsp_message));
   if (msg) {
     memset(msg, 0, sizeof(rtsp_message));
@@ -776,6 +781,11 @@ rtsp_message *msg_init(void) {
     die("msg_init -- can not allocate memory for rtsp_message %d.", msg_indexes);
   }
   // debug(1,"msg_init -- create item %d.", msg->index_number);
+  
+  rc = pthread_mutex_unlock(&reference_counter_lock);
+  if (rc)
+    debug(1, "Error %d unlocking reference counter lock");
+
   return msg;
 }
 
@@ -1707,6 +1717,14 @@ void handle_get_info(__attribute((unused)) rtsp_conn_info *conn, rtsp_message *r
     } else {
       void *qualifier_response_data = NULL;
       size_t qualifier_response_data_length = 0;
+      
+      
+      pthread_rwlock_rdlock(&principal_conn_lock); // don't let the principal_conn be changed
+      pthread_cleanup_push(rwlock_unlock,(void *)&principal_conn_lock);
+
+      
+      
+      
       if (add_pstring_to_malloc("acl=0", &qualifier_response_data,
                                 &qualifier_response_data_length) == 0)
         debug(1, "Problem");
@@ -1769,6 +1787,7 @@ void handle_get_info(__attribute((unused)) rtsp_conn_info *conn, rtsp_message *r
       free(vs);
       // pkString_make(pkString, sizeof(pkString), config.airplay_device_id);
       // plist_dict_set_item(response_plist, "pk", plist_new_string(pkString));
+      pthread_cleanup_pop(1); // release the principal_conn lock
       plist_to_bin(response_plist, &resp->content, &resp->contentlength);
       if (resp->contentlength == 0)
         debug(1, "GET /info Stage 1: response bplist not created!");
@@ -2685,11 +2704,6 @@ void teardown_phase_two(rtsp_conn_info *conn) {
 #endif
     }
     conn->groupContainsGroupLeader = 0;
-    config.airplay_statusflags &= (0xffffffff - (1 << 11)); // DeviceSupportsRelay
-    build_bonjour_strings(NULL);
-    debug(2, "Connection %d: TEARDOWN mdns_update on %s.", conn->connection_number,
-          get_category_string(conn->airplay_stream_category));
-    mdns_update(NULL, secondary_txt_records);
     if (conn->dacp_active_remote != NULL) {
       free(conn->dacp_active_remote);
       conn->dacp_active_remote = NULL;
@@ -2701,13 +2715,11 @@ void teardown_phase_two(rtsp_conn_info *conn) {
 void handle_teardown_2(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message *req,
                        rtsp_message *resp) {
 
-  debug(2, "Connection %d: TEARDOWN %s.", conn->connection_number,
+  debug(1, "Connection %d: TEARDOWN %s.", conn->connection_number,
         get_category_string(conn->airplay_stream_category));
   debug_log_rtsp_message(2, "TEARDOWN: ", req);
-  // if (have_player(conn)) {
   resp->respcode = 200;
   msg_add_header(resp, "Connection", "close");
-
   plist_t messagePlist = plist_from_rtsp_content(req);
   if (messagePlist != NULL) {
     // now see if the incoming plist contains a "streams" array
@@ -2726,6 +2738,18 @@ void handle_teardown_2(rtsp_conn_info *conn, __attribute__((unused)) rtsp_messag
             get_category_string(conn->airplay_stream_category));
       teardown_phase_one(conn); // try to do phase one anyway
       teardown_phase_two(conn);
+      
+      // only update these things if you're (still) the principal conn
+      pthread_rwlock_rdlock(&principal_conn_lock); // don't let the principal_conn be changed
+      pthread_cleanup_push(rwlock_unlock,(void *)&principal_conn_lock);
+      if ((principal_conn == conn) && (conn->airplay_stream_category == ptp_stream)) {
+        config.airplay_statusflags &= (0xffffffff - (1 << 11)); // DeviceSupportsRelay
+        build_bonjour_strings(conn);
+        debug(2, "Connection %d: TEARDOWN mdns_update on %s.", conn->connection_number,
+              get_category_string(conn->airplay_stream_category));
+        mdns_update(NULL, secondary_txt_records);
+      }
+      pthread_cleanup_pop(1); // release the principal_conn lock
       debug(2, "Connection %d: TEARDOWN %s -- close the connection complete",
             conn->connection_number, get_category_string(conn->airplay_stream_category));
       release_play_lock(conn);
@@ -2737,6 +2761,7 @@ void handle_teardown_2(rtsp_conn_info *conn, __attribute__((unused)) rtsp_messag
     debug(1, "Connection %d: missing plist!", conn->connection_number);
     resp->respcode = 451; // don't know what to do here
   }
+
   // debug(1,"Bogus exit for valgrind -- remember to comment it out!.");
   // exit(EXIT_SUCCESS); //
 }
@@ -3081,12 +3106,18 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
                                                   conn->connection_number); // kill all the other
                  listeners
               */
+              // only update these things if you're (still) the principal conn
+              pthread_rwlock_wrlock(&principal_conn_lock); // don't let the principal_conn be changed
+              pthread_cleanup_push(rwlock_unlock,(void *)&principal_conn_lock);
+              if (principal_conn == conn) {
+                    config.airplay_statusflags |= 1 << 11; // DeviceSupportsRelay
+                    build_bonjour_strings(conn);
+                    debug(2, "Connection %d: SETUP mdns_update on %s.", conn->connection_number,
+                          get_category_string(conn->airplay_stream_category));
+                    mdns_update(NULL, secondary_txt_records);
+              }
+              pthread_cleanup_pop(1); // release the principal_conn lock
 
-              config.airplay_statusflags |= 1 << 11; // DeviceSupportsRelay
-              build_bonjour_strings(conn);
-              debug(2, "Connection %d: SETUP mdns_update on %s.", conn->connection_number,
-                    get_category_string(conn->airplay_stream_category));
-              mdns_update(NULL, secondary_txt_records);
               resp->respcode = 200;
             } else {
               debug(1, "SETUP on Connection %d: PTP setup -- no timingPeerInfo plist.",
@@ -3580,8 +3611,8 @@ void handle_set_parameter_parameter(rtsp_conn_info *conn, rtsp_message *req,
         shairport_sync_set_volume(shairportSyncSkeleton, volume);
       } else {
 #endif
-        pthread_cleanup_debug_mutex_lock(&principal_conn_lock, 100000,
-                                         1); // don't let the principal_conn be changed
+        pthread_rwlock_rdlock(&principal_conn_lock); // don't let the principal_conn be changed
+        pthread_cleanup_push(rwlock_unlock,(void *)&principal_conn_lock);
         if (principal_conn == conn) {
           player_volume(volume, conn);
         }