2222
2323#include <stdio.h>
2424#include <unistd.h>
25+ #include <assert.h>
2526
2627#include <curl/curl.h>
2728
@@ -563,25 +564,338 @@ chMonitorBuildSocketCmd(virDomainObjPtr vm, const char *socket_path)
563564 return cmd ;
564565}
565566
567+ static const char * virCHMonitorEventStrings [] = {
568+ "vmm:starting" ,
569+ "vmm:shutdown" ,
570+ "vm:booting" , "vm:booted" ,
571+ "vm:pausing" , "vm:paused" ,
572+ "vm:resuming" , "vm:resumed" ,
573+ "vm:snapshotting" , "vm:snapshotted" ,
574+ "vm:restoring" , "vm:restored" ,
575+ "vm:resizing" , "vm:resized" ,
576+ "vm:shutdown" , "vm:deleted" ,
577+ "cpu_manager:create_vcpu" ,
578+ "virtio-device:activated" , "virtio-device:reset"
579+ };
580+
581+ static virCHMonitorEvent virCHMonitorEventFromString (const char * sourceStr ,
582+ const char * eventStr )
583+ {
584+ int i ;
585+ g_auto (virBuffer ) buf = VIR_BUFFER_INITIALIZER ;
586+ const char * event ;
587+
588+ virBufferAsprintf (& buf , "%s:%s" , sourceStr , eventStr );
589+ event = virBufferCurrentContent (& buf );
590+
591+ for (i = 0 ; i < virCHMonitorEventMax ; i ++ ) {
592+ if STREQ (event , virCHMonitorEventStrings [i ])
593+ break ;
594+ }
595+
596+ return i ;
597+ }
598+
599+ static int virCHMonitorValidateEventsJSON (char * buffer , size_t sz ,
600+ bool * incomplete )
601+ {
602+ /*
603+ * Marks the start of a JSON doc(starting with '{')
604+ */
605+ char * json_start = buffer ;
606+ /*
607+ * Marks the start of the buffer where the scan starts.
608+ * It could be either:
609+ * - Start of the buffer. Or
610+ * - Next location after a valid JSON doc.
611+ */
612+ char * scan_start = buffer ;
613+ int blocks = 0 ;
614+ int events = 0 ;
615+ int i = 0 ;
616+
617+ if (sz == 0 )
618+ return 0 ;
619+
620+ /*
621+ * Check if the message is a wellformed JSON. Try to find all
622+ * wellformed JSON doc and adjust the buffer accordingly by
623+ * removing invalid snippets in the buffer.
624+ */
625+ do {
626+ if (buffer [i ] == '{' ) {
627+ blocks ++ ;
628+
629+ if (blocks != 1 )
630+ continue ;
631+
632+ /*
633+ * Possible start of a valid JSON doc. Check if
634+ * there were any white characters or garbage
635+ * before the JSON doc at this location.
636+ */
637+ json_start = buffer + i ;
638+ if (scan_start != json_start ) {
639+ int invalid_chars = json_start - scan_start ;
640+ VIR_WARN ("invalid json or white chars in buffer: %.*s" ,
641+ invalid_chars , scan_start );
642+ memmove (scan_start , json_start , invalid_chars );
643+ }
644+ } else if (buffer [i ] == '}' && blocks != 0 ) {
645+ blocks -- ;
646+ if (blocks == 0 ) {
647+ events ++ ;
648+ /*
649+ * This location marks the end of a valid JSON doc.
650+ * Reset the scan_start to next location.
651+ */
652+ scan_start = buffer + i + 1 ;
653+ }
654+ }
655+ } while (++ i < sz );
656+
657+ * incomplete = blocks != 0 ? true : false;
658+ VIR_DEBUG ("Total events received: %d, incomplete: %d" ,
659+ events , * incomplete );
660+
661+ return events ;
662+ }
663+
664+ /*
665+ * Caller should have reference on Monitor and Domain
666+ */
667+ static int virCHMonitorProcessEvent (virCHMonitorPtr mon ,
668+ virJSONValuePtr eventJSON )
669+ {
670+ const char * event ;
671+ const char * source ;
672+ virDomainObjPtr vm = mon -> vm ;
673+ virCHMonitorEvent ev ;
674+
675+ if (virJSONValueObjectHasKey (eventJSON , "source" ) == 0 ) {
676+ VIR_WARN ("Invalid JSON from monitor, no source key" );
677+ return -1 ;
678+ }
679+ if (virJSONValueObjectHasKey (eventJSON , "event" ) == 0 ) {
680+ VIR_WARN ("Invalid JSON from monitor, no event key" );
681+ return -1 ;
682+ }
683+ source = virJSONValueObjectGetString (eventJSON , "source" );
684+ event = virJSONValueObjectGetString (eventJSON , "event" );
685+
686+ ev = virCHMonitorEventFromString (source , event );
687+ VIR_DEBUG ("Source: %s Event: %s, ev: %d" , source , event , ev );
688+ switch (ev ) {
689+ case virCHMonitorVmEventBooted :
690+ case virCHMonitorVmEventResumed :
691+ case virCHMonitorVmEventRestored :
692+ case virCHMonitorVirtioDeviceEventActivated :
693+ case virCHMonitorVirtioDeviceEventReset :
694+ virObjectLock (vm );
695+ if (virDomainObjIsActive (vm ) &&
696+ virCHDomainObjBeginJob (vm , CH_JOB_MODIFY ) == 0 ) {
697+ virCHProcessSetupThreads (vm );
698+ virCHDomainObjEndJob (vm );
699+ }
700+ virObjectUnlock (vm );
701+ break ;
702+ case virCHMonitorVmmEventShutdown : // shutdown inside vmm
703+ case virCHMonitorVmEventShutdown :
704+ {
705+ virCHDriverPtr driver = CH_DOMAIN_PRIVATE (vm )-> driver ;
706+ virDomainState state ;
707+
708+ virObjectLock (vm );
709+ state = virDomainObjGetState (vm , NULL );
710+ if ((ev == virCHMonitorVmmEventShutdown ||
711+ state == VIR_DOMAIN_SHUTDOWN ) &&
712+ (virCHDomainObjBeginJob (vm , CH_JOB_MODIFY ) == 0 )) {
713+
714+ virCHProcessStop (driver , vm , VIR_DOMAIN_SHUTOFF_SHUTDOWN );
715+
716+ virCHDomainObjEndJob (vm );
717+ }
718+ virObjectUnlock (vm );
719+ break ;
720+ }
721+ case virCHMonitorVmEventBooting :
722+ case virCHMonitorVmEventPausing :
723+ case virCHMonitorVmEventPaused :
724+ case virCHMonitorVmEventResuming :
725+ case virCHMonitorVmEventSnapshotting :
726+ case virCHMonitorVmEventSnapshotted :
727+ case virCHMonitorVmEventRestoring :
728+ case virCHMonitorVmEventResizing :
729+ case virCHMonitorVmEventResized :
730+ case virCHMonitorVmEventDeleted :
731+ case virCHMonitorVmmEventStarting :
732+ case virCHMonitorCpuCreateVcpu :
733+ break ;
734+ case virCHMonitorEventMax :
735+ default :
736+ VIR_WARN ("unkown event from monitor!" );
737+ break ;
738+ }
739+
740+ return 0 ;
741+ }
742+
743+ /*
744+ * Helper function to find a block of valid JSON
745+ * from a stream of multiple JSON blocks.
746+ */
747+ static inline char * end_of_json (char * str , size_t len )
748+ {
749+ bool started = false;
750+ int blocks = 0 ;
751+ int i = 0 ;
752+
753+ while ((i < len ) && (!started || blocks > 0 )) {
754+ if (str [i ] == '{' ) {
755+ if (!started )
756+ started = true;
757+ blocks ++ ;
758+ } else if (str [i ] == '}' ) {
759+ blocks -- ;
760+ }
761+ i ++ ;
762+ }
763+
764+ return (i == len && blocks ) ? NULL : str + i ;
765+ }
766+
767+ /*
768+ * Caller should have reference on Monitor and Domain
769+ */
770+ static int virCHMonitorProcessEvents (virCHMonitorPtr mon , int events )
771+ {
772+ virJSONValuePtr obj = NULL ;
773+ char * buf = mon -> buffer ;
774+ int ret = 0 ;
775+ int i = 0 ;
776+ size_t sz ;
777+
778+ for (i = 0 ; i < events ; i ++ ) {
779+ char tmp ;
780+ char * end = end_of_json (buf , mon -> buf_fill_sz );
781+
782+ /*
783+ * end should never be NULL! We validated that there
784+ * is a valid JSON document before calling end_of_json,
785+ * and end_of_json returns NULL only if it cannot find
786+ * a valid JSON document.
787+ */
788+ assert (end );
789+
790+ #pragma GCC diagnostic push
791+ #pragma GCC diagnostic ignored "-Wnull-dereference"
792+ tmp = * end , * end = 0 ;
793+ #pragma GCC diagnostic pop
794+
795+ if ((obj = virJSONValueFromString (buf ))) {
796+ if (virCHMonitorProcessEvent (mon , obj ) < 0 ) {
797+ VIR_WARN ("Failed to process the event!" );
798+ ret = -1 ;
799+ }
800+ virJSONValueFree (obj );
801+ } else {
802+ VIR_WARN ("Invalid JSON from monitor" );
803+ ret = -1 ;
804+ }
805+
806+ * end = tmp ;
807+ buf = end ;
808+ }
809+
810+ /*
811+ * If the buffer still has incomplete data, lets
812+ * push it to the beginning.
813+ */
814+ sz = buf - mon -> buffer ;
815+ if (sz < mon -> buf_fill_sz ) {
816+ mon -> buf_offset = mon -> buf_fill_sz - sz ;
817+ memmove (mon -> buffer , buf , sz );
818+ } else {
819+ mon -> buf_offset = 0 ;
820+ }
821+
822+ return ret ;
823+ }
824+
825+ static int virCHMonitorReadProcessEvents (virCHMonitorPtr mon )
826+ {
827+ size_t max_sz = CH_MONITOR_BUFFER_SZ - mon -> buf_offset ;
828+ char * buf = mon -> buffer + mon -> buf_offset ;
829+ virDomainObjPtr vm = mon -> vm ;
830+ bool incomplete = false;
831+ int events = 0 ;
832+ size_t sz = 0 ;
833+
834+ memset (buf , 0 , max_sz );
835+ do {
836+ ssize_t ret ;
837+
838+ ret = read (mon -> monitor_fd , buf + sz , max_sz - sz );
839+ if (ret == 0 || (ret < 0 && errno == EINTR )) {
840+ g_usleep (G_USEC_PER_SEC );
841+ continue ;
842+ } else if (ret < 0 ) {
843+ /*
844+ * We should never reach here. read(2) says possible errors
845+ * are EINTR, EAGAIN, EBADF, EFAULT, EINVAL, EIO, EISDIR
846+ * We handle EINTR gracefully. There is some serious issue
847+ * if we encounter any of the other errors(either in our code
848+ * or in the system). Better to bail out.
849+ */
850+ VIR_ERROR ("Failed to read monitor events!: %s" , strerror (errno ));
851+ abort ();
852+ }
853+
854+ sz += ret ;
855+ events = virCHMonitorValidateEventsJSON (mon -> buffer ,
856+ mon -> buf_offset + sz , & incomplete );
857+ VIR_DEBUG ("Monitor event(%ld):\n%s" , sz , mon -> buffer );
858+
859+ } while (virDomainObjIsActive (vm ) && (sz < max_sz ) &&
860+ (events == 0 || incomplete ));
861+
862+ /*
863+ * We process the events from the read buffer if
864+ * - There is atleast one event in the buffer
865+ * - No incomplete events in the buffer or
866+ * - The buffer is full and may have incomplete entries.
867+ *
868+ * If the buffer is full, virCHMonitorProcessEvents processes
869+ * the completed events in the buffer and moves incomplete
870+ * entries to the start of the buffer and next read from the pipe
871+ * starts from the offset.
872+ */
873+ mon -> buf_fill_sz = sz + mon -> buf_offset ;
874+ return (events > 0 ) ? virCHMonitorProcessEvents (mon , events ) : events ;
875+ }
876+
566877static void virCHMonitorEventLoop (void * data )
567878{
568879 virCHMonitorPtr mon = (virCHMonitorPtr )data ;
569880 virDomainObjPtr vm = mon -> vm ;
570881
571882 VIR_DEBUG ("Monitor event loop thread starting" );
572883
884+ mon -> buffer = g_malloc_n (sizeof (char ), CH_MONITOR_BUFFER_SZ );
885+ mon -> buf_offset = 0 ;
886+ mon -> buf_fill_sz = 0 ;
887+
573888 virObjectRef (vm );
574889 while (g_atomic_int_get (& mon -> event_loop_stop ) == 0 ) {
575-
576- g_usleep (G_USEC_PER_SEC );
577-
578- virObjectLock (vm );
579- if (virDomainObjIsActive (vm ) &&
580- virCHDomainObjBeginJob (vm , CH_JOB_MODIFY ) == 0 ) {
581- virCHProcessSetupThreads (vm );
582- virCHDomainObjEndJob (vm );
583- }
584- virObjectUnlock (vm );
890+ VIR_DEBUG ("Reading events from monitor.." );
891+ /*
892+ * virCHMonitorReadProcessEvents errors out only if
893+ * virjson detects an invalid JSON doc and the buffer
894+ * in that case is automatically taken care of. We can
895+ * safely continue.
896+ */
897+ if (virCHMonitorReadProcessEvents (mon ) < 0 )
898+ VIR_WARN ("Failed to process events from monitor!" );
585899 }
586900 virObjectUnref (vm );
587901
@@ -793,7 +1107,8 @@ void virCHMonitorClose(virCHMonitorPtr mon)
7931107 curl_easy_cleanup (mon -> handle );
7941108
7951109 if (mon -> socketpath ) {
796- if (virFileRemove (mon -> socketpath , -1 , -1 ) < 0 ) {
1110+ if (virFileExists (mon -> socketpath ) &&
1111+ virFileRemove (mon -> socketpath , -1 , -1 ) < 0 ) {
7971112 VIR_WARN ("Unable to remove CH socket file '%s'" ,
7981113 mon -> socketpath );
7991114 }
@@ -1209,7 +1524,7 @@ virCHMonitorRefreshThreadInfo(virCHMonitorPtr mon)
12091524 continue ;
12101525 }
12111526
1212- VIR_INFO ("VM PID: %d, TID %d, COMM: %s" ,
1527+ VIR_DEBUG ("VM PID: %d, TID %d, COMM: %s" ,
12131528 (int )vm -> pid , (int )tids [i ], data );
12141529 if (STRPREFIX (data , "vcpu" )) {
12151530 int index ;
@@ -1220,7 +1535,7 @@ virCHMonitorRefreshThreadInfo(virCHMonitorPtr mon)
12201535 info [i ].type = virCHThreadTypeVcpu ;
12211536 info [i ].vcpuInfo .online = true;
12221537 info [i ].vcpuInfo .cpuid = index ;
1223- VIR_INFO ("vcpu%d -> tid: %d" , index , tids [i ]);
1538+ VIR_DEBUG ("vcpu%d -> tid: %d" , index , tids [i ]);
12241539 } else if (STRPREFIX (data , "virtio" )) {
12251540 info [i ].type = virCHThreadTypeIO ;
12261541 strncpy (info [i ].ioInfo .thrName , data , VIRCH_THREAD_NAME_LEN - 1 );
0 commit comments