Wednesday, March 20, 2013

Android on FreeBSD

I have been doing Android development on and off for about 12 months now and since I don't run Linux, Windows or OS X anywhere I've had to resort to using VBox and X11 forwarding.
VirtualBox, although fantastic with what it does, is still not fast enough and I've found the more I code, the more frustrated I get with the poor performance of my setup.
Unfortunately there's no native Android SDK for FreeBSD, but thanks to the FreeBSD linuxulator we can get some near-native performance with the Android SDK.for Linux

I've decided to ditch the recommended IDE ( Eclipse ) and go with  IntelliJ ( http://www.jetbrains.com/idea/ ) -- it comes highly recommended by many Java developers and seems a lot less 'buggy' than Eclipse. Luckily for me it's written in java and runs quite happily on FreeBSD 9.1 with java/linux-sun-jdk16, I have tried a few other Java versions, but this one seems to work the best.


Here's a pic of JB 4.2.2 running with IntelliJ 12.0.4 (Community Version).



There are two things that aren't working quite the way they should.
IntelliJ is calling the 64bit emulator binary, instead of the 32, and this isn't executable by the linuxulator as it only supports 32bit bins. I have worked around this by linking the 64bit bin to the 32bit.

ADB also isn't working through IntelliJ. I haven't quite figured out why this is, but watch this space - I'll post an update when I figure it out. Until then it's ant and adb from CLI!


$ ant debug
Buildfile: /home/ash/IdeaProjects/untitled2/build.xml
-set-mode-check:
-set-debug-files:
-check-env:
 [checkenv] Android SDK Tools Revision 21.1.0
 [checkenv] Installed at /home/ash/adt-bundle-linux-x86-20130219/sdk
...
debug:,
BUILD SUCCESSFUL
Total time: 4 seconds

$ ~/adt-bundle-linux-x86-20130219/sdk/platform-tools/adb devices
List of devices attached
emulator-5554   device
$ ~/adt-bundle-linux-x86-20130219/sdk/platform-tools/adb install *.apk
1787 KB/s (36918 bytes in 0.020s)
        pkg: /data/local/tmp/untitled2-debug-unaligned.apk
Success


*EDIT: Seems changing over to OpenJDK6 has fixed the 'adb' connectivity  issues

Wednesday, May 30, 2012

MythTV 0.25 Released

MythTV 0.25 has been released and contains a much awaited array of patches, features and code consolidations.
I haven't had much time to play with all the new features yet, but I have had time to re-work my previous patch ( http://vt-100.blogspot.com.au/2012/01/mythtv-lastwatched.html ) to add the 'last watched' feature to this release.


Here it is:


--- /home/ash/src/mythtv-0.25/libs/libmyth/programinfo.cpp      2012-04-10 15:29:22.000000000 +1000
+++ libs/libmyth/programinfo.cpp        2012-05-29 07:35:50.000000000 +1000
@@ -2601,14 +2601,11 @@ void ProgramInfo::SaveWatched(bool watch
         MSqlQuery query(MSqlQuery::InitCon());
         query.prepare("UPDATE videometadata"
-                    " SET watched = :WATCHEDFLAG"
-                    " WHERE title = :TITLE"
-                    " AND subtitle = :SUBTITLE"
-                    " AND filename = :FILENAME ;");
-        query.bindValue(":TITLE", title);
-        query.bindValue(":SUBTITLE", subtitle);
-        query.bindValue(":FILENAME", url);
+                    " SET watched = :WATCHEDFLAG,"
+                   " lastwatched = NOW()"
+                    " WHERE filename = :FILENAME ;");
         query.bindValue(":WATCHEDFLAG", watched);
+        query.bindValue(":FILENAME", url);
         if (!query.exec())
             MythDB::DBError("Set watched flag", query);
--- /home/ash/src/mythtv-0.25/libs/libmythmetadata/metadatacommon.cpp   2012-04-10 15:29:22.000000000 +1000
+++ libs/libmythmetadata/metadatacommon.cpp     2012-05-29 08:24:51.000000000 +1000
@@ -68,6 +68,7 @@ MetadataLookup::MetadataLookup(void) :
     m_collectionref(),
     m_tmsref(),
     m_imdb(),
+    m_lastwatched(),
     m_people(),
     m_studios(),
     m_homepage(),
@@ -137,6 +138,7 @@ MetadataLookup::MetadataLookup(
     const QString &collectionref,
     const QString &tmsref,
     const QString &imdb,
+    const QDateTime lastwatched,
     const PeopleMap people,
     const QStringList &studios,
     const QString &homepage,
@@ -202,6 +204,7 @@ MetadataLookup::MetadataLookup(
     m_collectionref(collectionref),
     m_tmsref(tmsref),
     m_imdb(imdb),
+    m_lastwatched(lastwatched),
     m_people(people),
     m_studios(studios),
     m_homepage(homepage),
@@ -383,6 +386,8 @@ ArtworkList MetadataLookup::GetArtwork(V
 void MetadataLookup::toMap(MetadataMap &metadataMap)
 {
+
+    QString dateformat = gCoreContext->GetSetting("DateFormat", "yyyy-MM-dd hh:mm");
     metadataMap["filename"] = m_filename;
     metadataMap["title"] = m_title;
     metadataMap["network"] = m_network;
@@ -422,6 +427,7 @@ void MetadataLookup::toMap(MetadataMap &
     metadataMap["releasedate"] = MythDateToString(m_releasedate, kDateFull);
     metadataMap["lastupdated"] = MythDateTimeToString(m_lastupdated, kDateFull);
+    metadataMap["lastwatched"] = m_lastwatched.toString(dateformat);
     metadataMap["runtime"] = QObject::tr("%n minute(s)", "", m_runtime);
     metadataMap["runtimesecs"] = QObject::tr("%n second(s)", "", m_runtimesecs);
@@ -941,7 +947,7 @@ MetadataLookup* ParseMetadataItem(const
     QStringList categories, countries, studios;
     float userrating = 0;
     QDate releasedate;
-    QDateTime lastupdated, startts, endts, recstartts, recendts;
+    QDateTime lastupdated, lastwatched, startts, endts, recstartts, recendts;
     PeopleMap people;
     ArtworkMap artwork;
@@ -991,6 +997,8 @@ MetadataLookup* ParseMetadataItem(const
     lastupdated = RFC822TimeToQDateTime(item.
                       firstChildElement("lastupdated").text());
+    lastwatched = RFC822TimeToQDateTime(item.firstChildElement("lastwatched").text());
+
     userrating = item.firstChildElement("userrating").text().toFloat();
     ratingcount = item.firstChildElement("ratingcount").text().toUInt();
     tracknum = item.firstChildElement("tracknum").text().toUInt();
@@ -1136,7 +1144,7 @@ MetadataLookup* ParseMetadataItem(const
         audioproperties, videoproperties, subtitletype, certification,
         countries, popularity, budget, revenue, album, tracknum, system, year,
         releasedate, lastupdated, runtime, runtimesecs, inetref, collectionref,
-        tmsref, imdb, people, studios, homepage, trailerURL, artwork, DownloadMap());
+        tmsref, imdb, lastwatched, people, studios, homepage, trailerURL, artwork, DownloadMap());
 }
 MetadataLookup* ParseMetadataMovieNFO(const QDomElement& item,
--- /home/ash/src/mythtv-0.25/libs/libmythmetadata/videometadatalistmanager.cpp 2012-04-10 15:29:22.000000000 +1000
+++ libs/libmythmetadata/videometadatalistmanager.cpp   2012-05-29 17:44:33.000000000 +1000
@@ -130,7 +130,7 @@ void VideoMetadataListManager::loadAllFr
         "userrating, length, playcount, filename, hash, showlevel, "
         "coverfile, inetref, collectionref, homepage, childid, browse, watched, "
         "playcommand, category, intid, trailer, screenshot, banner, fanart, "
-        "subtitle, tagline, season, episode, host, insertdate, processed, "
+        "subtitle, tagline, season, episode, host, insertdate, processed, lastwatched, "
         "contenttype FROM videometadata ");
     if (!sql.isEmpty())
--- /home/ash/src/mythtv-0.25/libs/libmythmetadata/videometadata.h      2012-04-10 15:29:22.000000000 +1000
+++ libs/libmythmetadata/videometadata.h        2012-05-29 07:43:15.000000000 +1000
@@ -83,6 +83,7 @@ class META_PUBLIC VideoMetadata
              int season = 0,
              int episode = 0,
              const QDate &insertdate = QDate(),
+            const QDateTime &lastwatched = QDateTime(),
              int id = 0,
              ParentalLevel::Level showlevel = ParentalLevel::plLowest,
              int categoryID = 0,
@@ -180,6 +181,8 @@ class META_PUBLIC VideoMetadata
     bool GetProcessed() const;
     void SetProcessed(bool processed);
+    QDateTime GetLastwatched() const;
+
     VideoContentType GetContentType() const;
     void SetContentType(VideoContentType contenttype);
--- /home/ash/src/mythtv-0.25/libs/libmythmetadata/metadatacommon.h     2012-04-10 15:29:22.000000000 +1000
+++ libs/libmythmetadata/metadatacommon.h       2012-05-29 07:52:42.000000000 +1000
@@ -144,6 +144,7 @@ class META_PUBLIC MetadataLookup : publi
         const QString &collectionref,
         const QString &tmsref,
         const QString &imdb,
+       const QDateTime lastwatched,
         const PeopleMap people,
         const QStringList &studios,
         const QString &homepage,
@@ -430,6 +431,8 @@ class META_PUBLIC MetadataLookup : publi
     QString m_tmsref;
     QString m_imdb;
+    const QDateTime m_lastwatched;
+
     // People - Video
     const PeopleMap m_people;
     const QStringList m_studios;
--- /home/ash/src/mythtv-0.25/libs/libmythmetadata/videometadata.cpp    2012-04-10 15:29:22.000000000 +1000
+++ libs/libmythmetadata/videometadata.cpp      2012-05-29 17:47:26.000000000 +1000
@@ -103,6 +103,7 @@ class VideoMetadataImp
              const QString &plot, float userrating,
              const QString &rating, int length, int playcount,
              int season, int episode, const QDate &insertdate,
+            const QDateTime &lastwatched,
              int id, ParentalLevel::Level showlevel, int categoryID,
              int childID, bool browse, bool watched,
              const QString &playcommand, const QString &category,
@@ -121,7 +122,7 @@ class VideoMetadataImp
         m_screenshot(screenshot), m_banner(banner), m_fanart(fanart),
         m_host(host), m_categoryID(categoryID), m_childID(childID),
         m_year(year), m_releasedate(releasedate), m_length(length), m_playcount(playcount),
-        m_season(season), m_episode(episode), m_insertdate(insertdate), m_showlevel(showlevel),
+        m_season(season), m_episode(episode), m_insertdate(insertdate), m_lastwatched(lastwatched),m_showlevel(showlevel),
         m_browse(browse), m_watched(watched), m_id(id),
         m_userrating(userrating), m_processed(processed),
         m_contenttype(contenttype)
@@ -182,6 +183,7 @@ class VideoMetadataImp
             m_userrating = rhs.m_userrating;
             m_host = rhs.m_host;
             m_processed = rhs.m_processed;
+           m_lastwatched = rhs.m_lastwatched;
             m_contenttype = rhs.m_contenttype;
             // No DB vars
@@ -334,6 +336,8 @@ class VideoMetadataImp
     bool GetProcessed() const { return m_processed; }
     void SetProcessed(bool processed) { m_processed = processed; }
+    QDateTime GetLastwatched() const { return m_lastwatched; }
+
     VideoContentType GetContentType() const { return m_contenttype; }
     void SetContentType(VideoContentType contenttype) { m_contenttype = contenttype; }
@@ -394,6 +398,7 @@ class VideoMetadataImp
     int m_season;
     int m_episode;
     QDate m_insertdate;
+    QDateTime m_lastwatched;
     ParentalLevel::Level m_showlevel;
     bool m_browse;
     bool m_watched;
@@ -484,7 +489,7 @@ void VideoMetadataImp::Reset()
                     QString(), VIDEO_PLOT_DEFAULT, 0.0,
                     VIDEO_RATING_DEFAULT, 0, 0,
                     VideoMetadata::FilenameToMeta(m_filename, 2).toInt(),
-                    VideoMetadata::FilenameToMeta(m_filename, 3).toInt(), QDate(), m_id,
+                    VideoMetadata::FilenameToMeta(m_filename, 3).toInt(), QDate(),QDateTime(), m_id,
                     ParentalLevel::plLowest, 0, -1, true, false, "", "",
                     VideoMetadata::genre_list(), VideoMetadata::country_list(),
                     VideoMetadata::cast_list(), m_host, false);
@@ -596,8 +601,9 @@ void VideoMetadataImp::fromDBRow(MSqlQue
     m_host = query.value(31).toString();
     m_insertdate = query.value(32).toDate();
     m_processed = query.value(33).toBool();
+    m_lastwatched = query.value(34).toDateTime();
-    m_contenttype = ContentTypeFromString(query.value(34).toString());
+    m_contenttype = ContentTypeFromString(query.value(35).toString());
     VideoCategory::GetCategory().get(m_categoryID, m_category);
@@ -1110,7 +1116,7 @@ VideoMetadata::VideoMetadata(const QStri
              int year, const QDate &releasedate, const QString &inetref, int collectionref,
              const QString &homepage, const QString &director, const QString &studio,
              const QString &plot, float userrating, const QString &rating,
-             int length, int playcount, int season, int episode, const QDate &insertdate,
+             int length, int playcount, int season, int episode, const QDate &insertdate,  const QDateTime &lastwatched,
              int id, ParentalLevel::Level showlevel, int categoryID,
              int childID, bool browse, bool watched,
              const QString &playcommand, const QString &category,
@@ -1123,7 +1129,7 @@ VideoMetadata::VideoMetadata(const QStri
     m_imp = new VideoMetadataImp(filename, hash, trailer, coverfile, screenshot, banner,
                             fanart, title, subtitle, tagline, year, releasedate, inetref,
                             collectionref, homepage, director, studio, plot, userrating, rating,
-                            length, playcount, season, episode, insertdate, id, showlevel, categoryID,
+                            length, playcount, season, episode, insertdate, lastwatched, id, showlevel, categoryID,
                             childID, browse, watched, playcommand, category, genres, countries,
                             cast, host, processed, contenttype);
 }
@@ -1207,6 +1213,7 @@ void VideoMetadata::toMap(MetadataMap &m
     metadataMap["browseable"] = GetDisplayBrowse(GetBrowse());
     metadataMap["watched"] = GetDisplayWatched(GetWatched());
     metadataMap["processed"] = GetDisplayProcessed(GetProcessed());
+    metadataMap["lastwatched"] = GetLastwatched().toString(gCoreContext->GetSetting("DateFormat"));
     metadataMap["category"] = GetCategory();
 }
@@ -1328,6 +1335,7 @@ void ClearMap(MetadataMap &metadataMap)
     metadataMap["watched"] = "";
     metadataMap["category"] = "";
     metadataMap["processed"] = "";
+    metadataMap["lastwatched"] = "";
 }
 bool VideoMetadata::HasSortKey() const
@@ -1335,6 +1343,12 @@ bool VideoMetadata::HasSortKey() const
     return m_imp->HasSortKey();
 }
+QDateTime VideoMetadata::GetLastwatched() const
+{
+    return m_imp->GetLastwatched();
+}
+
+
 const VideoMetadata::SortKey &VideoMetadata::GetSortKey() const
 {
     return m_imp->GetSortKey();


Tuesday, May 15, 2012

RBAC on Solaris

Introduction 

Role Based Access Control ( RBAC ) on Solaris aims at deprecating the 'sudo' command with a native framework. RBAC isn't very user intuitive and isn't really an elegant approach to access control, but it does work quite well and once you've mastered the basics it'll all start making a bit more sense.

Firstly, some definitions:

Profile - A profile is a set of properties which reference one or many authorizations

Authorization - "An authorization is a discrete right that can be granted to a role or user. Authorizations are checked by RBAC-compliant applications before a user gets access to the application or specific operations within it. This check replaces the tests in conventional UNIX applications for UID=0 " ( http://docs.oracle.com/cd/E19683-01/817-0365/rbacref-21/index.html)

Roles - Similar to a 'user', a role is created for a user to escalate to in order to perform a specific operation

There's an array of default profiles and authorizations in Solaris, they are definitely worth researching and understanding. The relvant files/directories are:

  • /etc/security/prof_attr.d/
  • /etc/security/exec_attr.d/ 
  • /etc/security/auth_attr.d/ 

 Every user created on the system, by default, will be assigned the profiles "All" ( to run all commands ) and "Basic Solaris User" (allows escalated access for cd-related commands in order to interact with the cd drive)
You can change these default profiles by changing the relevant variables in /etc/security/policy.conf :

# egrep "AUTH|PROF" /etc/security/policy.conf
AUTHS_GRANTED=
PROFS_GRANTED=Basic Solaris User

Using RBAC 

As a very basic example of RBAC, say you have a user 'williama' who is a new administrator. You want to grant this administrator access to the user maintenance commands - useradd, usermod, groupadd etc. Here's how you'd go about it:

Check the user doesn't already have the access:
# profiles -l williama
williama:
      Basic Solaris User
        auths=solaris.mail.mailq,solaris.device.mount.removable,solaris.admin.wusb.read
        profiles=All
          /usr/bin/cdrecord.bin      privs=file_dac_read,sys_devices,proc_lock_memory,proc_priocntl,net_privaddr
          /usr/bin/readcd.bin        privs=file_dac_read,sys_devices,net_privaddr
          /usr/bin/cdda2wav.bin      privs=file_dac_read,sys_devices,proc_priocntl,net_privaddr
      All
          *
As this is a system command, it most likely already has a profile and authorization somewhere:

# cd /etc/security
# egrep "useradd|usermod|groupmod" exec_attr.d/*
exec_attr.d/core-os:User Management:solaris:cmd:RO::/usr/sbin/useradd:euid=0
exec_attr.d/core-os:User Management:solaris:cmd:RO::/usr/sbin/usermod:euid=0
exec_attr.d/core-os:User Management:solaris:cmd:RO::/usr/sbin/groupmod:euid=0
exec_attr.d/core-os:User Security:solaris:cmd:RO::/usr/sbin/usermod:euid=0
exec_attr.d/core-os:User Security:solaris:cmd:RO::/usr/sbin/groupmod:euid=0

There we go, it's called "User Management". Now just modify the user's account to include this profile.

# usermod -P "User Management" williama
Found user in files repository.
Check that it all worked.


# profiles williama
williama:
          User Management
          Basic Solaris User
          All

# profiles -l williama
williama:
      User Management
        auths=solaris.user.manage,solaris.role.manage,solaris.group.manage,solaris.project.delegate,solaris.account.activate
          /usr/sbin/grpck            euid=0
          /usr/sbin/pwck             euid=0
          /usr/sbin/useradd          euid=0
          /usr/sbin/userdel          euid=0
          /usr/sbin/usermod          euid=0
          /usr/sbin/roleadd          euid=0
          /usr/sbin/roledel          euid=0
          /usr/sbin/rolemod          euid=0
          /usr/sbin/groupadd         euid=0
          /usr/sbin/groupdel         euid=0
          /usr/sbin/groupmod         euid=0
          /usr/bin/passwd            euid=0
      Basic Solaris User
        auths=solaris.mail.mailq,solaris.device.mount.removable,solaris.admin.wusb.read
        profiles=All
          /usr/bin/cdrecord.bin      privs=file_dac_read,sys_devices,proc_lock_memory,proc_priocntl,net_privaddr
          /usr/bin/readcd.bin        privs=file_dac_read,sys_devices,net_privaddr
          /usr/bin/cdda2wav.bin      privs=file_dac_read,sys_devices,proc_priocntl,net_privaddr
      All
          *

# su - williama
.....
$ pfexec /usr/sbin/useradd -md /export/home/test test

$ id -a test
uid=60009(test) gid=10(staff) groups=10(staff)


Now that this administrator can add/remove users, you also want them to be able to reboot the machine.
As above, there is a pre-defined profile for this, but let's not use that for now...

Firstly, create a profile, giving it a name and short description:

# echo "REBOOT:::profile for rebooting:" >>  /etc/security/prof_attr

Create the exec attribute

# echo "REBOOT:solaris:cmd:::/usr/sbin/reboot:euid=0"  >>  /etc/security/exec_attr

Assign the profile to the user ( use the + here, so all the previous profiles don't get overwritten )

# usermod -P +REBOOT  williama

Check that it all works

# profiles -l williama
williama:
      User Management
        auths=solaris.user.manage,solaris.role.manage,solaris.group.manage,solaris.project.delegate,solaris.account.activate
          /usr/sbin/grpck            euid=0
          /usr/sbin/pwck             euid=0
          /usr/sbin/useradd          euid=0
          /usr/sbin/userdel          euid=0
          /usr/sbin/usermod          euid=0
          /usr/sbin/roleadd          euid=0
          /usr/sbin/roledel          euid=0
          /usr/sbin/rolemod          euid=0
          /usr/sbin/groupadd         euid=0
          /usr/sbin/groupdel         euid=0
          /usr/sbin/groupmod         euid=0
          /usr/bin/passwd            euid=0
      REBOOT
          /usr/sbin/reboot           euid=0

      Basic Solaris User
        auths=solaris.mail.mailq,solaris.device.mount.removable,solaris.admin.wusb.read
        profiles=All
          /usr/bin/cdrecord.bin      privs=file_dac_read,sys_devices,proc_lock_memory,proc_priocntl,net_privaddr
          /usr/bin/readcd.bin        privs=file_dac_read,sys_devices,net_privaddr
          /usr/bin/cdda2wav.bin      privs=file_dac_read,sys_devices,proc_priocntl,net_privaddr
      All
          *

Using Roles

There is another way to achieve the above and that is through the use of 'roles'. That is, you create a role, allow the user to 'su' to this role, then execute the relevant commands.

To start off, the user has no roles.

# roles williama
No roles

Create the 'reboot' role and assign it a password

# roleadd -m -d /export/home/reboot reboot
# passwd reboot
....

Assign the REBOOT profile ( created above ) to the role

# rolemod -P REBOOT reboot

Assign the user the 'reboot' role
# usermod -R reboot williama
# roles williama
reboot

Now, test.

$ su reboot
...
$ reboot

Monday, January 16, 2012

MythTV - 'lastwatched'

About six months ago I decided to finally consolidate all my stored media and organise a media box for myself. I dabbled with a few different applications and finally settled ,on a recommendation from a friend, on MythTV.

MythTV is a great application and can do a lot more than I'm using it for, but I've found it's pretty well supported, easy to setup and maintain, and has quite a usable and 'pretty' interface. It can also be controlled from my phone ( I haven't yet purchased a remote ), which is very handy too.

I'm running FreeBSD 9.0 RC2 with mythtv 0.24.1 from ports, along with a few home-grown patches. I haven't yet setup a tuner, but I intend to in the not too distant future.

There are a few little niggly things that are missing in MythTV and I have been slowly hacking away at the code to add them and correct a few other strange behaviours.

The following patch adds a 'lastwatched' field to the metadata along with a patch for the network remote ( this isn't the official one, it's just one I found online somewhere ).

**If you're going to patch MythTV with this patch, add an extra field to the 'videometadata' table named 'lastwatched' of type 'timestamp' ( not null default '0000-00-00 00:00:00' ... or something similar).




--- /usr/ports/multimedia/mythtv/work/mythtv-0.24.1/libs/libmythmetadata/metadatacommon.cpp     2011-05-16 06:57:52.000000000 +1000
+++ libs/libmythmetadata/metadatacommon.cpp 2011-10-31 20:05:30.000000000 +1000
@@ -39,6 +39,7 @@ MetadataLookup::MetadataLookup(void) :
m_inetref(),
m_tmsref(),
m_imdb(),
+ m_lastwatched(),
m_people(),
m_studios(),
m_homepage(),
@@ -81,7 +82,7 @@ MetadataLookup::MetadataLookup(
const uint runtimesecs,
QString inetref,
QString tmsref,
- QString imdb,
+ QString imdb,const QDateTime lastwatched,
const PeopleMap people,
const QStringList studios,
const QString homepage,
@@ -122,6 +123,7 @@ MetadataLookup::MetadataLookup(
m_inetref(inetref),
m_tmsref(tmsref),
m_imdb(imdb),
+ m_lastwatched(lastwatched),
m_people(people),
m_studios(studios),
m_homepage(homepage),
@@ -174,6 +176,7 @@ void MetadataLookup::toMap(MetadataMap &
"yyyy-MM-dd hh:mm");
metadataMap["releasedate"] = m_releasedate.toString(dateformat);
metadataMap["lastupdated"] = m_lastupdated.toString(dateformat);
+ metadataMap["lastwatched"] = m_lastwatched.toString(dateformat);

metadataMap["runtime"] = QString::number(m_runtime) +
QObject::tr(" Minutes");
@@ -204,6 +207,7 @@ MetadataLookup* ParseMetadataItem(const
float userrating = 0;
QDate releasedate;
QDateTime lastupdated;
+ QDateTime lastwatched;
PeopleMap people;
ArtworkMap artwork;

@@ -226,6 +230,9 @@ MetadataLookup* ParseMetadataItem(const
lastupdated = RFC822TimeToQDateTime(item.
firstChildElement("lastupdated").text());

+ lastwatched = RFC822TimeToQDateTime(item.
+ firstChildElement("lastwatched").text());
+
userrating = item.firstChildElement("userrating").text().toFloat();
tracknum = item.firstChildElement("tracknum").text().toUInt();
popularity = item.firstChildElement("popularity").text().toUInt();
@@ -365,8 +372,7 @@ MetadataLookup* ParseMetadataItem(const
tagline, description, season, episode, certification, countries,
popularity, budget, revenue, album, tracknum, system, year,
releasedate, lastupdated, runtime, runtimesecs, inetref,
- tmsref, imdb, people, studios, homepage, trailerURL, artwork,
- DownloadMap());
+ tmsref, imdb, lastwatched, people, studios, homepage, trailerURL, artwork, DownloadMap());
}

PeopleMap ParsePeople(QDomElement people)
--- /usr/ports/multimedia/mythtv/work/mythtv-0.24.1/libs/libmythmetadata/metadatacommon.h 2011-05-16 06:57:52.000000000 +1000
+++ libs/libmythmetadata/metadatacommon.h 2011-10-31 20:05:59.000000000 +1000
@@ -119,12 +119,13 @@ class MPUBLIC MetadataLookup : public QO
QString inetref,
QString tmsref,
QString imdb,
+ const QDateTime lastwatched,
const PeopleMap people,
const QStringList studios,
const QString homepage,
const QString trailerURL,
const ArtworkMap artwork,
- DownloadMap downloads);
+ DownloadMap downloads );

void toMap(MetadataMap &map);

@@ -273,6 +274,8 @@ class MPUBLIC MetadataLookup : public QO
QString m_tmsref;
QString m_imdb;

+ const QDateTime m_lastwatched;
+
// People - Video
const PeopleMap m_people;
const QStringList m_studios;
--- /usr/ports/multimedia/mythtv/work/mythtv-0.24.1/libs/libmyth/programinfo.cpp 2011-05-16 06:57:52.000000000 +1000
+++ libs/libmyth/programinfo.cpp 2011-10-31 17:32:40.000000000 +1000
@@ -2310,12 +2310,9 @@ void ProgramInfo::SaveWatched(bool watch

MSqlQuery query(MSqlQuery::InitCon());
query.prepare("UPDATE videometadata"
- " SET watched = :WATCHEDFLAG"
- " WHERE title = :TITLE"
- " AND subtitle = :SUBTITLE"
- " AND filename = :FILENAME ;");
- query.bindValue(":TITLE", title);
- query.bindValue(":SUBTITLE", subtitle);
+ " SET watched = :WATCHEDFLAG,"
+ " lastwatched = NOW()"
+ " WHERE filename = :FILENAME ;");
query.bindValue(":FILENAME", url);
query.bindValue(":WATCHEDFLAG", watched);

--- /usr/ports/multimedia/mythtv/work/mythtv-0.24.1/libs/libmythmetadata/videometadata.cpp 2011-05-16 06:57:52.000000000 +1000
+++ libs/libmythmetadata/videometadata.cpp 2011-11-03 10:58:37.000000000 +1000
@@ -100,6 +100,7 @@ class VideoMetadataImp
const QString &plot, float userrating,
const QString &rating, int length,
int season, int episode, const QDate &insertdate,
+ const QDateTime &lastwatched,
int id, ParentalLevel::Level showlevel, int categoryID,
int childID, bool browse, bool watched,
const QString &playcommand, const QString &category,
@@ -116,7 +117,7 @@ class VideoMetadataImp
m_screenshot(screenshot), m_banner(banner), m_fanart(fanart),
m_host(host), m_categoryID(categoryID), m_childID(childID),
m_year(year), m_releasedate(releasedate), m_length(length), m_season(season),
- m_episode(episode), m_insertdate(insertdate), m_showlevel(showlevel),
+ m_episode(episode), m_insertdate(insertdate), m_lastwatched(lastwatched), m_showlevel(showlevel),
m_browse(browse), m_watched(watched), m_id(id),
m_userrating(userrating), m_processed(processed)
{
@@ -174,6 +175,7 @@ class VideoMetadataImp
m_userrating = rhs.m_userrating;
m_host = rhs.m_host;
m_processed = rhs.m_processed;
+ m_lastwatched = rhs.m_lastwatched;

// No DB vars
m_sort_key = rhs.m_sort_key;
@@ -318,6 +320,8 @@ class VideoMetadataImp
bool GetProcessed() const { return m_processed; }
void SetProcessed(bool processed) { m_processed = processed; }

+ QDateTime GetLastwatched() const { return m_lastwatched; }
+
////////////////////////////////

void SaveToDatabase();
@@ -373,6 +377,7 @@ class VideoMetadataImp
int m_season;
int m_episode;
QDate m_insertdate;
+ QDateTime m_lastwatched;
ParentalLevel::Level m_showlevel;
bool m_browse;
bool m_watched;
@@ -463,7 +468,7 @@ void VideoMetadataImp::Reset()
QString(), VIDEO_PLOT_DEFAULT, 0.0,
VIDEO_RATING_DEFAULT, 0,
VideoMetadata::FilenameToMeta(m_filename, 2).toInt(),
- VideoMetadata::FilenameToMeta(m_filename, 3).toInt(), QDate(), m_id,
+ VideoMetadata::FilenameToMeta(m_filename, 3).toInt(), QDate(),QDateTime(), m_id,
ParentalLevel::plLowest, 0, -1, true, false, "", "",
VideoMetadata::genre_list(), VideoMetadata::country_list(),
VideoMetadata::cast_list(), m_host, false);
@@ -573,6 +578,7 @@ void VideoMetadataImp::fromDBRow(MSqlQue
m_host = query.value(29).toString();
m_insertdate = query.value(30).toDate();
m_processed = query.value(31).toBool();
+ m_lastwatched = query.value(32).toDateTime();

VideoCategory::GetCategory().get(m_categoryID, m_category);

@@ -1069,7 +1075,7 @@ VideoMetadata::VideoMetadata(const QStri
int year, const QDate &releasedate, const QString &inetref,
const QString &homepage, const QString &director, const QString &studio,
const QString &plot, float userrating, const QString &rating,
- int length, int season, int episode, const QDate &insertdate,
+ int length, int season, int episode, const QDate &insertdate, const QDateTime &lastwatched,
int id, ParentalLevel::Level showlevel, int categoryID,
int childID, bool browse, bool watched,
const QString &playcommand, const QString &category,
@@ -1081,7 +1087,7 @@ VideoMetadata::VideoMetadata(const QStri
m_imp = new VideoMetadataImp(filename, hash, trailer, coverfile, screenshot, banner,
fanart, title, subtitle, tagline, year, releasedate, inetref,
homepage, director, studio, plot, userrating, rating, length,
- season, episode, insertdate, id, showlevel, categoryID, childID,
+ season, episode, insertdate, lastwatched, id, showlevel, categoryID, childID,
browse, watched, playcommand, category, genres, countries, cast,
host, processed);
}
@@ -1222,6 +1228,7 @@ void VideoMetadata::toMap(MetadataMap &m
metadataMap["browseable"] = GetDisplayBrowse(GetBrowse());
metadataMap["watched"] = GetDisplayWatched(GetWatched());
metadataMap["processed"] = GetDisplayProcessed(GetProcessed());
+ metadataMap["lastwatched"] = GetLastwatched().toString(gCoreContext->GetSetting("DateFormat"));
metadataMap["category"] = GetCategory();
}

@@ -1262,6 +1269,7 @@ void ClearMap(MetadataMap &metadataMap)
metadataMap["watched"] = "";
metadataMap["category"] = "";
metadataMap["processed"] = "";
+ metadataMap["lastwatched"] = "";
}

bool VideoMetadata::HasSortKey() const
@@ -1499,6 +1507,11 @@ void VideoMetadata::SetProcessed(bool pr
m_imp->SetProcessed(processed);
}

+QDateTime VideoMetadata::GetLastwatched() const
+{
+ return m_imp->GetLastwatched();
+}
+
const QString &VideoMetadata::GetPlayCommand() const
{
return m_imp->getPlayCommand();
--- /usr/ports/multimedia/mythtv/work/mythtv-0.24.1/libs/libmythmetadata/videometadata.h 2011-05-16 06:57:52.000000000 +1000
+++ libs/libmythmetadata/videometadata.h 2011-10-31 19:57:02.000000000 +1000
@@ -79,6 +79,7 @@ class MPUBLIC VideoMetadata
int season = 0,
int episode = 0,
const QDate &insertdate = QDate(),
+ const QDateTime &lastwatched = QDateTime(),
int id = 0,
ParentalLevel::Level showlevel = ParentalLevel::plLowest,
int categoryID = 0,
@@ -170,6 +171,8 @@ class MPUBLIC VideoMetadata
bool GetProcessed() const;
void SetProcessed(bool processed);

+ QDateTime GetLastwatched() const;
+
const QString &GetPlayCommand() const;
void SetPlayCommand(const QString &playCommand);

--- /usr/ports/multimedia/mythtv/work/mythtv-0.24.1/libs/libmythmetadata/videometadatalistmanager.cpp 2011-05-16 06:57:52.000000000 +1000
+++ libs/libmythmetadata/videometadatalistmanager.cpp 2011-10-31 17:46:15.000000000 +1000
@@ -115,7 +115,7 @@ void VideoMetadataListManager::loadAllFr
"userrating, length, filename, hash, showlevel, "
"coverfile, inetref, homepage, childid, browse, watched, "
"playcommand, category, intid, trailer, screenshot, banner, fanart, "
- "subtitle, tagline, season, episode, host, insertdate, processed "
+ "subtitle, tagline, season, episode, host, insertdate, processed,lastwatched "
" FROM videometadata");

query.prepare(BaseMetadataQuery);
--- /usr/ports/multimedia/mythtv/work/mythtv-0.24.1/libs/libmythmetadata/videoscan.cpp 2011-05-16 06:57:52.000000000 +1000
+++ ./libs/libmythmetadata/videoscan.cpp 2011-10-31 19:58:26.000000000 +1000
@@ -278,7 +278,7 @@ class VideoScannerThread : public QThrea
0.0, VIDEO_RATING_DEFAULT, 0,
VideoMetadata::FilenameToMeta(p->first, 2).toInt(),
VideoMetadata::FilenameToMeta(p->first, 3).toInt(),
- QDate::currentDate(),
+ QDate::currentDate(), QDateTime::currentDateTime(),
0, ParentalLevel::plLowest);

VERBOSE(VB_GENERAL, QString("Adding : %1 : %2 : %3")
--- /usr/ports/multimedia/mythtv/work/mythtv-0.24.1/programs/mythfrontend/networkcontrol.cpp 2011-05-16 06:57:52.000000000 +1000
+++ programs/mythfrontend/networkcontrol.cpp 2011-11-26 11:20:51.000000000 +1000
@@ -1186,7 +1186,9 @@ void NetworkControl::customEvent(QEvent
NetworkControlClient * ncc = nc->getClient();
if (ncc)
{
- sendReplyToClient(ncc, reply);
+ int index = clients.indexOf(ncc);
+ if (index >= 0)
+ sendReplyToClient(ncc, reply);
}
else //send to all clients
{

Monday, November 21, 2011

IPS Solaris 11

Solaris 11, released on 09-11-2011, has introduced and whole array of new commands and technologies that I am really looking forward to playing with. There's been a replacement of the very buggy live upgrade (lu*) process with beadm, a complete overhaul of the TCP/IP stack, multiple ZFS enhancements and also a few Zone enhancements.

The most notable change for me is the introduction of IPS and the imminent deprecation of SVR4 packages. I have a bit of a love-hate relationship with IPS. I understand the theory behind it, but I don't entirely agree with the way it's been implemented. Nevertheless, I have to learn it and I have to use it.

I'm just going outline a few administrative processes which I've used and thought might be helpsome one.

The basic administrative commands are pretty self explanitory:

  • pkg search
  • pkg install
  • pkg update
  • pkg contents
  • pkg uninstall
  • etc ....

I have setup a local offline repository for testing-- in fact this is probably how I'll end up using the repository for the production systems as none of our unix servers have net access.

The repository can be downloaded from the Oracle website here - http://www.oracle.com/technetwork/server-storage/solaris11/downloads/index.html .There's quite good instructions on the site which explains how to set it up, so I won't bother going through it.

One thing that wasn't documented was the patch method for a repo. As I'm not mirroring the oracle repo, I have to manually patch it myself with the SRUs.
This is done with the 'pkgrevc' command:

# unzip p13399198_1100_SOLARIS64.zip

Archive: p13399198_1100_SOLARIS64.zip

inflating: readme.html

inflating: readme.txt

inflating: sol-11-1111-sru1-incr-repo.iso

# lofiadm -a `pwd`/sol-11-1111-sru1-incr-repo.iso

# mount -F hsfs /dev/lofi/1 /mnt/

# pkgrecv -s file:///mnt/repo -d file:///export/Solaris11/repo -m all-versions '*'


You can check your local Vs remote repo with 'pkg search' ( '-r' for remote, '-l' for local )

# pkg info -r entire

Name: entire

Summary: entire incorporation including Support Repository Update (Oracle Solaris 11 11/11 SRU 01). For more information see https://support.oracle.com/CSP/main/article?cmd=show&type=NOT&doctype=REFERENCE&id=1372094.1

...

Version: 0.5.11

Build Release: 5.11

Branch: 0.175.0.1.0.4.0

Packaging Date: November 10, 2011 10:56:28 PM

Size: 5.45 kB

FMRI: pkg://solaris/entire@0.5.11,5.11-0.175.0.1.0.4.0:20111110T225628Z



# pkg info -l entire

Name: entire

Summary: Incorporation to lock all system packages to the same build

...

Build Release: 5.11

Branch: 0.175.0.0.0.2.0

Packaging Date: October 20, 2011 02:38:22 PM

Size: 5.45 kB

FMRI: pkg://solaris/entire@0.5.11,5.11-0.175.0.0.0.2.0:20111020T143822Z

Then upgrade with 'pkg upgrade -vn' ( always do a dummy run ) then 'pkg upgrade -v'.


You can also check your repository information using 'pkgrepo':

# pkgrepo info -s file:///export/Solaris11_repo/repo/

PUBLISHER PACKAGES STATUS UPDATED

solaris 4292 online 2011-11-18T04:47:31.737612Z

Sunday, September 11, 2011

WebScarab and JSON

Webscarab is a Java based proxy server which intercepts HTTP and HTTPS requests for security diagnostics and troubleshooting, it's useful for analysing your webpage to see how your data is flowing.

I've recently been using Webscarab with some GWT applications, but ran into problems with a few RPC calls. The regular POSTS and GETS were being intercepted correctly, but not the JSON replies.

As the JSON replies are being sent as Content-Type 'gzip', it is a simple task of changing the headers to be "text/xml".

Webscarab offers a feature called 'bean-shell', it's used for automated header and content manipulation, as well as many other things. You can find it under the 'Proxy' Tab on the main Webscarab window.
The code I used to do re-write the header is this:

import org.owasp.webscarab.model.Request;
import org.owasp.webscarab.model.Response;
import org.owasp.webscarab.httpclient.HTTPClient;
import java.io.IOException;

public Response fetchResponse(HTTPClient nextPlugin, Request request) throws IOException {
response = nextPlugin.fetchResponse(request);
response.setHeader("Content-Type","text/xml");
return response;
}


Adding this code and enabling the bean-shell feature, means that all replies will come through as text/xml, so it'll pop-up like a regular response, allowing you to analyse and manipulate the content as you see fit.

Tuesday, August 30, 2011

Broken system after zpool upgrade

After updating to Solaris 9 Update 9 ( 144500-19 ), I went ahead to upgrade my zfs pools ( zpool upgrade -a ) and zfs file systems (zfs upgrade -a ). The upgrade went pretty well until I rebooted and was faced with the following:

Rebooting with command: boot /pci@0,600000/pci@0/pci@0/scsi@0/disk@0,0 -F
Boot device: /pci@0,600000/pci@0/pci@0/scsi@0/disk@0,0 File and args: -F
ERROR: Last Trap: Fast Data Access MMU Miss
%TL:1 %TT:68 %TPC:f000ad64 %TnPC:f000ad68 %TSTATE:6a001600
%PSTATE:16 ( IE:1 PRIV:1 PEF:1 )
DSFSR:4280804b ( FV:1 OW:1 PR:1 E:1 TM:1 ASI:80 NC:1 BERR:1 )
DSFAR:fda60000 DSFPAR:4018007f8000 D-TAG:1fb6c2000


For some reason the start of the disk has been corrupted - I don't know how this wasn't picked up during in QA at Oracle, but it seems to have been a problem since 144500-06 ( http://wesunsolve.net/bugid/id/7022082 ). I also saw a reference to a similar bug in the OpenSolaris bug tracker.

There isn't really a "fix", as far as I can see, but there are two work-arounds:

1. reinstall the boot block after you've done a zpool upgrade, but before you reboot:

$ installboot -F zfs /usr/platform/`uname -i`/lib/fs/zfs/bootblk /dev/rdsk/c0t1d0s0

2. Patch the miniroot on your jumpstart server, run a 'boot net -s' on your broken system and reinstall the boot block in your miniroot environment